X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.modeling.ui%2Fsrc%2Forg%2Fsimantics%2Fmodeling%2Fui%2Fdiagram%2FSliderClass.java;h=60f28f17b9efa222bb9e6320c09586a51451fe30;hp=c00195b53cf93530f81c10142a6db2351bf49f56;hb=fe9ef5837d666acca45b00de17b94034f8edcee3;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/SliderClass.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/SliderClass.java index c00195b53..60f28f17b 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/SliderClass.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/SliderClass.java @@ -1,482 +1,480 @@ -/******************************************************************************* - * Copyright (c) 2007, 2010 Association for Decentralized Information Management - * in Industry THTH ry. - * 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: - * VTT Technical Research Centre of Finland - initial API and implementation - *******************************************************************************/ -package org.simantics.modeling.ui.diagram; - -import java.awt.Graphics2D; -import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.util.Collection; -import java.util.EnumSet; - -import javax.swing.JSlider; -import javax.vecmath.Vector2d; - -import org.simantics.g2d.diagram.IDiagram; -import org.simantics.g2d.element.ElementClass; -import org.simantics.g2d.element.ElementHints; -import org.simantics.g2d.element.ElementUtils; -import org.simantics.g2d.element.IElement; -import org.simantics.g2d.element.SceneGraphNodeKey; -import org.simantics.g2d.element.handler.ElementHandler; -import org.simantics.g2d.element.handler.InternalSize; -import org.simantics.g2d.element.handler.LifeCycle; -import org.simantics.g2d.element.handler.Move; -import org.simantics.g2d.element.handler.Outline; -import org.simantics.g2d.element.handler.Rotate; -import org.simantics.g2d.element.handler.Scale; -import org.simantics.g2d.element.handler.SceneGraph; -import org.simantics.g2d.element.handler.StaticSymbol; -import org.simantics.g2d.element.handler.TextEditor; -import org.simantics.g2d.element.handler.Transform; -import org.simantics.g2d.element.handler.TextEditor.Modifier; -import org.simantics.g2d.element.handler.impl.BorderColorImpl; -import org.simantics.g2d.element.handler.impl.SimpleElementLayers; -import org.simantics.g2d.element.handler.impl.StaticSymbolImpl; -import org.simantics.g2d.element.handler.impl.TextColorImpl; -import org.simantics.g2d.element.handler.impl.TextEditorImpl; -import org.simantics.g2d.element.handler.impl.TextFontImpl; -import org.simantics.g2d.element.handler.impl.TextImpl; -import org.simantics.g2d.elementclass.MonitorHandler; -import org.simantics.g2d.image.Image; -import org.simantics.g2d.image.ProviderUtils; -import org.simantics.g2d.image.impl.AbstractImage; -import org.simantics.scenegraph.Node; -import org.simantics.scenegraph.g2d.G2DParentNode; -import org.simantics.scenegraph.swing.SliderNode; -import org.simantics.utils.datastructures.cache.IFactory; -import org.simantics.utils.datastructures.cache.IProvider; -import org.simantics.utils.datastructures.cache.ProvisionException; -import org.simantics.utils.datastructures.hints.IHintContext.Key; -import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; -import org.simantics.utils.strings.format.MetricsFormat; -import org.simantics.utils.strings.format.MetricsFormatList; - -/** - * @author J-P Laine - */ -public class SliderClass { - public static final Key KEY_SLIDER_RESOURCE_PATH = new KeyOf(Collection.class, "SLIDER_RESOURCE_PATH"); - public static final Key KEY_SLIDER_COMPONENT = new KeyOf(Object.class, "SLIDER_COMPONENT"); - public static final Key KEY_SLIDER_SUFFIX = new KeyOf(String.class, "SLIDER_SUFFIX"); - public static final Key KEY_SLIDER_RANGE = new KeyOf(Range.class, "SLIDER_SUBSTITUTIONS"); - public static final Key KEY_SLIDER_GC = new KeyOf(Graphics2D.class, "SLIDER_GC"); - public static final Key KEY_SLIDER_VALUE = new KeyOf(Double.class, "SLIDER_VALUE"); - public static final Key KEY_TOOLTIP_TEXT = new KeyOf(String.class, "TOOLTIP_TEXT"); - public static final Key KEY_NUMBER_FORMAT = new KeyOf(MetricsFormat.class, "NUMBER_FORMAT"); - - /** - * If this hint is defined, the monitor will force its x-axis to match this - * angle. If this hint doesn't exist, the monitor will not force x-axis - * orientation. - */ - public static final Key KEY_DIRECTION = new KeyOf(Double.class, "SLIDER_DIRECTION"); - - public static final Key KEY_SG_NODE = new SceneGraphNodeKey(Node.class, "SLIDER_SG_NODE"); - public final static MetricsFormat DEFAULT_NUMBER_FORMAT = MetricsFormatList.METRICS_DECIMAL; - - public static class Range { - T min; - T max; - public Range(T min, T max) { - this.min = min; - this.max = max; - } - - public T getMin() { - return min; - } - - public T getMax() { - return max; - } - } - - static MetricsFormat getNumberFormat(IElement e) { - return ElementUtils.getHintOrDefault(e, KEY_NUMBER_FORMAT, DEFAULT_NUMBER_FORMAT); - } - - static void setNumberFormat(IElement e, MetricsFormat f) { - ElementUtils.setOrRemoveHint(e, KEY_NUMBER_FORMAT, f); - } - - public static class SliderHandlerImpl implements MonitorHandler { - private static final long serialVersionUID = -4258875745321808416L; - public static final MonitorHandler INSTANCE = new SliderHandlerImpl(); - } - - static class Initializer implements LifeCycle { - private static final long serialVersionUID = 4404942036933073584L; - - IElement parentElement; - Range range; - Object component; - String suffix; - Double value; - Boolean hack; - - Initializer(IElement parentElement, Range range, Double value, Object component, String suffix, Boolean hack) { - this.parentElement = parentElement; - this.range = range; - this.component = component; - this.suffix = suffix; - this.value = value; - this.hack = hack; - } - - @Override - public void onElementActivated(IDiagram d, IElement e) { - if(!hack) { - hack = true; - Point2D parentPos = ElementUtils.getPos(parentElement); - Point2D thisPos = ElementUtils.getPos(e); - Move move = e.getElementClass().getSingleItem(Move.class); - move.moveTo(e, thisPos.getX() - parentPos.getX(), thisPos.getY() - parentPos.getY()); - } - } - - @Override - public void onElementCreated(IElement e) { - if(parentElement != null) e.setHint(ElementHints.KEY_PARENT_ELEMENT, parentElement); - if(range != null) e.setHint(KEY_SLIDER_RANGE, range); - if(component != null) e.setHint(KEY_SLIDER_COMPONENT, component); - if(suffix != null) e.setHint(KEY_SLIDER_SUFFIX, suffix); - if(value != null) e.setHint(KEY_SLIDER_VALUE, value); - - e.setHint(KEY_DIRECTION, 0.0); - e.setHint(KEY_NUMBER_FORMAT, DEFAULT_NUMBER_FORMAT); - } - @Override - public void onElementDeactivated(IDiagram d, IElement e) { - } - @Override - public void onElementDestroyed(IElement e) { - } - }; - - public static void update(IElement e) { - SliderSGNode node = e.getElementClass().getAtMostOneItemOfClass(SliderSGNode.class); - node.update(e); - } - - public static void cleanup(IElement e) { - SliderSGNode node = e.getElementClass().getAtMostOneItemOfClass(SliderSGNode.class); - node.cleanup(e); - } - - public static class SliderSGNode implements SceneGraph, InternalSize, Outline { - private static final long serialVersionUID = -106278359626957687L; - - public static final SliderSGNode INSTANCE = new SliderSGNode(); - - @Override - public void init(final IElement e, final G2DParentNode parent) { - // Create node if it doesn't exist yet - SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE); - if(node == null || node.getBounds() == null || node.getParent() != parent) { - node = parent.addNode(ElementUtils.generateNodeId(e), SliderNode.class); - e.setHint(KEY_SG_NODE, node); - node.setActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent event) { - TextEditor editor = e.getElementClass().getAtMostOneItemOfClass(TextEditor.class); - if(editor != null) { - Modifier modifier = editor.getModifier(e); - if(modifier != null) - modifier.modify(e, event.getActionCommand()); - } - }}); - node.setBounds(new Rectangle2D.Double(0, 0, 32, 100)); - -// Rectangle2D bounds = (Rectangle2D)e.getHint(ElementHints.KEY_BOUNDS); -// if(bounds != null) node.setBounds(bounds); -// String tooltip = e.getHint(KEY_TOOLTIP_TEXT); - Range range = e.getHint(KEY_SLIDER_RANGE); - Double value = e.getHint(KEY_SLIDER_VALUE); - - node.setMinimum((int)Math.round(range.getMin())); // FIXME - node.setMaximum((int)Math.round(range.getMax())); - node.setValue((int)Math.round(value)); - node.setMajorTickSpacing(10); - node.setMinorTickSpacing(5); - node.setPaintLabels(true); - node.setOrientation(JSlider.VERTICAL); - } - update(e); - } - - public void update(IElement e) { - Double value = e.getHint(KEY_SLIDER_VALUE); - - SliderNode node = (SliderNode) e.getHint(KEY_SG_NODE); - if (node != null && value != null) { - node.setValue((int)Math.round(value)); - } - } - - @Override - public void cleanup(IElement e) { - SliderNode node = (SliderNode)e.removeHint(KEY_SG_NODE); - if (node != null) - node.remove(); - } - - @Override - public Rectangle2D getBounds(IElement e, Rectangle2D size) { - Rectangle2D shape = new Rectangle2D.Double(0, 0, 0, 0); - - SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE); - if(node != null && node.getBounds() != null) { - shape = node.getBounds().getBounds2D(); - } - - if(size != null) size.setRect(shape); - return shape; - } - - @Override - public Shape getElementShape(IElement e) { - Shape shape = new Rectangle2D.Double(0, 0, 0, 0); - - SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE); - if(node != null && node.getBounds() != null) { - shape = node.getBounds(); - } - - return shape; - } - - } - - public static class Transformer implements Transform, Move, Rotate, Scale { - - private static final long serialVersionUID = -3704887325602085677L; - - public static final Transformer INSTANCE = new Transformer(null); - - Double aspectRatio; - - public Transformer() { - this(null); - } - - public Transformer(Double aspectRatio) { - this.aspectRatio = aspectRatio; - } - - @Override - public Double getFixedAspectRatio(IElement e) { - return aspectRatio; - } - - @Override - public Point2D getScale(IElement e) { - AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); - return _getScale(at); - } - - @Override - public void setScale(IElement e, Point2D newScale) { - // Doesn't work for monitors. - Point2D oldScale = getScale(e); - double sx = newScale.getX() / oldScale.getX(); - double sy = newScale.getY() / oldScale.getY(); - AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); - at = new AffineTransform(at); - at.scale(sx, sy); - e.setHint(ElementHints.KEY_TRANSFORM, at); - } - - @Override - public Point2D getMaximumScale(IElement e) { - return null; - } - - @Override - public Point2D getMinimumScale(IElement e) { - return null; - } - - private static Point2D _getScale(AffineTransform at) { - double m00 = at.getScaleX(); - double m11 = at.getScaleY(); - double m10 = at.getShearY(); - double m01 = at.getShearX(); - // Project unit vector to canvas - double sx = Math.sqrt(m00 * m00 + m10 * m10); - double sy = Math.sqrt(m01 * m01 + m11 * m11); - return new Point2D.Double(sx, sy); - } - - @Override - public void rotate(IElement e, double theta, Point2D origin) { - if (Double.isNaN(theta)) return; - theta = Math.toDegrees(theta); - Double angle = e.getHint(KEY_DIRECTION); - double newAngle = angle != null ? angle+theta : theta; - newAngle = Math.IEEEremainder(newAngle, 360.0); - e.setHint(KEY_DIRECTION, newAngle); - } - - @Override - public double getAngle(IElement e) { - Double angle = e.getHint(KEY_DIRECTION); - return angle != null ? Math.toRadians(angle) : 0; - } - - @Override - public Point2D getPosition(IElement e) { - AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); - Point2D p = new Point2D.Double(at.getTranslateX(), at.getTranslateY()); - return p; - } - - @Override - public void moveTo(IElement e, double x, double y) { - AffineTransform origAt = e.getHint(ElementHints.KEY_TRANSFORM); - double oldX = origAt.getTranslateX(); - double oldY = origAt.getTranslateY(); - AffineTransform move = AffineTransform.getTranslateInstance(x-oldX, y-oldY); - AffineTransform at2 = new AffineTransform(origAt); - at2.preConcatenate(move); - e.setHint(ElementHints.KEY_TRANSFORM, at2); - } - - @Override - public AffineTransform getTransform(IElement e) { - AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); - - IElement parentElement = e.getHint(ElementHints.KEY_PARENT_ELEMENT); - if (parentElement == null) - return at; - - Transform parentTransform = parentElement.getElementClass().getSingleItem(Transform.class); - assert(parentTransform!=null); - - AffineTransform result = (AffineTransform)at.clone(); - result.preConcatenate(parentTransform.getTransform(parentElement)); - - return result; - } - - @Override - public void setTransform(IElement e, AffineTransform at) { - e.setHint(ElementHints.KEY_TRANSFORM, at.clone()); - } - } - - static double getOrientationDelta(IElement e, AffineTransform tr) { - Double angle = e.getHint(KEY_DIRECTION); - if (angle == null || Double.isNaN(angle)) - return Double.NaN; - double angrad = Math.toRadians(angle); - - Vector2d forcedAxis = new Vector2d(Math.cos(angrad), Math.sin(angrad)); - Vector2d x = new Vector2d(tr.getScaleX(), tr.getShearX()); - forcedAxis.normalize(); - x.normalize(); - double cosa = forcedAxis.dot(x); - double delta = Math.acos(cosa); - return delta; - } - - static class SliderImageFactory implements IFactory { - private double staticScaleX = 1, staticScaleY = 1; - - public SliderImageFactory(double staticScaleX, double staticScaleY) { - this.staticScaleX = staticScaleX; - this.staticScaleY = staticScaleY; - } - - @Override - public Image get() throws ProvisionException { - return new AbstractImage() { - Shape path = new Rectangle2D.Double(0, 0, 50*staticScaleX, 22*staticScaleY); - - @Override - public Rectangle2D getBounds() { - return path.getBounds2D(); - } - - @Override - public EnumSet getFeatures() { - return EnumSet.of(Feature.Vector); - } - - @Override - public Shape getOutline() { - return path; - } - - @Override - public Node init(G2DParentNode parent) { - SliderNode node = parent.getOrCreateNode(""+hashCode(), SliderNode.class); - node.setValue(0); - node.setBounds(new Rectangle2D.Double(0, 0, 50, 22)); - node.setTransform(AffineTransform.getScaleInstance(staticScaleX, staticScaleY)); - return node; - } - }; - } - } - - static final IProvider SLIDER_IMAGE = - ProviderUtils.reference( - ProviderUtils.cache( - ProviderUtils.rasterize( - new SliderImageFactory(0.5, 0.5) - ))); - - static final StaticSymbol SLIDER_SYMBOL = new StaticSymbolImpl( SLIDER_IMAGE.get() ); - - public static final ElementClass SLIDER_CLASS = - ElementClass.compile( - SliderHandlerImpl.INSTANCE, - Transformer.INSTANCE, - BorderColorImpl.BLACK, - SliderSGNode.INSTANCE, - TextImpl.INSTANCE, - TextEditorImpl.INSTANCE, - TextFontImpl.DEFAULT, - TextColorImpl.BLACK, - SimpleElementLayers.INSTANCE, - SLIDER_SYMBOL - ); - - // staticScale{X,Y} define the scale of the static monitor image - public static ElementClass create(IElement parentElement, Range range, Double value, Object component, String suffix, double staticScaleX, double staticScaleY, ElementHandler... extraHandlers) { - // Bit of a hack to be able to define the scale - IProvider staticSliderSymbolProvider = ProviderUtils.reference( - ProviderUtils.cache( - ProviderUtils - .rasterize( - new SliderImageFactory(staticScaleX, staticScaleY)))); - StaticSymbol staticSliderSymbol = new StaticSymbolImpl( staticSliderSymbolProvider.get() ); - return ElementClass.compile( - new Initializer(parentElement, range, value, component, suffix, parentElement != null ? false : true), - SliderHandlerImpl.INSTANCE, - Transformer.INSTANCE, - BorderColorImpl.BLACK, - SliderSGNode.INSTANCE, - TextImpl.INSTANCE, - TextEditorImpl.INSTANCE, - TextFontImpl.DEFAULT, - TextColorImpl.BLACK, - SimpleElementLayers.INSTANCE, - staticSliderSymbol - ).newClassWith(extraHandlers); - } - -} +/******************************************************************************* + * Copyright (c) 2007, 2010 Association for Decentralized Information Management + * in Industry THTH ry. + * 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: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.modeling.ui.diagram; + +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Collection; +import java.util.EnumSet; + +import javax.swing.JSlider; + +import org.apache.commons.math3.geometry.euclidean.twod.Vector2D; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.element.ElementClass; +import org.simantics.g2d.element.ElementHints; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.SceneGraphNodeKey; +import org.simantics.g2d.element.handler.ElementHandler; +import org.simantics.g2d.element.handler.InternalSize; +import org.simantics.g2d.element.handler.LifeCycle; +import org.simantics.g2d.element.handler.Move; +import org.simantics.g2d.element.handler.Outline; +import org.simantics.g2d.element.handler.Rotate; +import org.simantics.g2d.element.handler.Scale; +import org.simantics.g2d.element.handler.SceneGraph; +import org.simantics.g2d.element.handler.StaticSymbol; +import org.simantics.g2d.element.handler.TextEditor; +import org.simantics.g2d.element.handler.Transform; +import org.simantics.g2d.element.handler.TextEditor.Modifier; +import org.simantics.g2d.element.handler.impl.BorderColorImpl; +import org.simantics.g2d.element.handler.impl.SimpleElementLayers; +import org.simantics.g2d.element.handler.impl.StaticSymbolImpl; +import org.simantics.g2d.element.handler.impl.TextColorImpl; +import org.simantics.g2d.element.handler.impl.TextEditorImpl; +import org.simantics.g2d.element.handler.impl.TextFontImpl; +import org.simantics.g2d.element.handler.impl.TextImpl; +import org.simantics.g2d.elementclass.MonitorHandler; +import org.simantics.g2d.image.Image; +import org.simantics.g2d.image.ProviderUtils; +import org.simantics.g2d.image.impl.AbstractImage; +import org.simantics.scenegraph.Node; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.swing.SliderNode; +import org.simantics.utils.datastructures.cache.IFactory; +import org.simantics.utils.datastructures.cache.IProvider; +import org.simantics.utils.datastructures.cache.ProvisionException; +import org.simantics.utils.datastructures.hints.IHintContext.Key; +import org.simantics.utils.datastructures.hints.IHintContext.KeyOf; +import org.simantics.utils.strings.format.MetricsFormat; +import org.simantics.utils.strings.format.MetricsFormatList; + +/** + * @author J-P Laine + */ +public class SliderClass { + public static final Key KEY_SLIDER_RESOURCE_PATH = new KeyOf(Collection.class, "SLIDER_RESOURCE_PATH"); + public static final Key KEY_SLIDER_COMPONENT = new KeyOf(Object.class, "SLIDER_COMPONENT"); + public static final Key KEY_SLIDER_SUFFIX = new KeyOf(String.class, "SLIDER_SUFFIX"); + public static final Key KEY_SLIDER_RANGE = new KeyOf(Range.class, "SLIDER_SUBSTITUTIONS"); + public static final Key KEY_SLIDER_GC = new KeyOf(Graphics2D.class, "SLIDER_GC"); + public static final Key KEY_SLIDER_VALUE = new KeyOf(Double.class, "SLIDER_VALUE"); + public static final Key KEY_TOOLTIP_TEXT = new KeyOf(String.class, "TOOLTIP_TEXT"); + public static final Key KEY_NUMBER_FORMAT = new KeyOf(MetricsFormat.class, "NUMBER_FORMAT"); + + /** + * If this hint is defined, the monitor will force its x-axis to match this + * angle. If this hint doesn't exist, the monitor will not force x-axis + * orientation. + */ + public static final Key KEY_DIRECTION = new KeyOf(Double.class, "SLIDER_DIRECTION"); + + public static final Key KEY_SG_NODE = new SceneGraphNodeKey(Node.class, "SLIDER_SG_NODE"); + public final static MetricsFormat DEFAULT_NUMBER_FORMAT = MetricsFormatList.METRICS_DECIMAL; + + public static class Range { + T min; + T max; + public Range(T min, T max) { + this.min = min; + this.max = max; + } + + public T getMin() { + return min; + } + + public T getMax() { + return max; + } + } + + static MetricsFormat getNumberFormat(IElement e) { + return ElementUtils.getHintOrDefault(e, KEY_NUMBER_FORMAT, DEFAULT_NUMBER_FORMAT); + } + + static void setNumberFormat(IElement e, MetricsFormat f) { + ElementUtils.setOrRemoveHint(e, KEY_NUMBER_FORMAT, f); + } + + public static class SliderHandlerImpl implements MonitorHandler { + private static final long serialVersionUID = -4258875745321808416L; + public static final MonitorHandler INSTANCE = new SliderHandlerImpl(); + } + + static class Initializer implements LifeCycle { + private static final long serialVersionUID = 4404942036933073584L; + + IElement parentElement; + Range range; + Object component; + String suffix; + Double value; + Boolean hack; + + Initializer(IElement parentElement, Range range, Double value, Object component, String suffix, Boolean hack) { + this.parentElement = parentElement; + this.range = range; + this.component = component; + this.suffix = suffix; + this.value = value; + this.hack = hack; + } + + @Override + public void onElementActivated(IDiagram d, IElement e) { + if(!hack) { + hack = true; + Point2D parentPos = ElementUtils.getPos(parentElement); + Point2D thisPos = ElementUtils.getPos(e); + Move move = e.getElementClass().getSingleItem(Move.class); + move.moveTo(e, thisPos.getX() - parentPos.getX(), thisPos.getY() - parentPos.getY()); + } + } + + @Override + public void onElementCreated(IElement e) { + if(parentElement != null) e.setHint(ElementHints.KEY_PARENT_ELEMENT, parentElement); + if(range != null) e.setHint(KEY_SLIDER_RANGE, range); + if(component != null) e.setHint(KEY_SLIDER_COMPONENT, component); + if(suffix != null) e.setHint(KEY_SLIDER_SUFFIX, suffix); + if(value != null) e.setHint(KEY_SLIDER_VALUE, value); + + e.setHint(KEY_DIRECTION, 0.0); + e.setHint(KEY_NUMBER_FORMAT, DEFAULT_NUMBER_FORMAT); + } + @Override + public void onElementDeactivated(IDiagram d, IElement e) { + } + @Override + public void onElementDestroyed(IElement e) { + } + }; + + public static void update(IElement e) { + SliderSGNode node = e.getElementClass().getAtMostOneItemOfClass(SliderSGNode.class); + node.update(e); + } + + public static void cleanup(IElement e) { + SliderSGNode node = e.getElementClass().getAtMostOneItemOfClass(SliderSGNode.class); + node.cleanup(e); + } + + public static class SliderSGNode implements SceneGraph, InternalSize, Outline { + private static final long serialVersionUID = -106278359626957687L; + + public static final SliderSGNode INSTANCE = new SliderSGNode(); + + @Override + public void init(final IElement e, final G2DParentNode parent) { + // Create node if it doesn't exist yet + SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE); + if(node == null || node.getBounds() == null || node.getParent() != parent) { + node = parent.addNode(ElementUtils.generateNodeId(e), SliderNode.class); + e.setHint(KEY_SG_NODE, node); + node.setActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent event) { + TextEditor editor = e.getElementClass().getAtMostOneItemOfClass(TextEditor.class); + if(editor != null) { + Modifier modifier = editor.getModifier(e); + if(modifier != null) + modifier.modify(e, event.getActionCommand()); + } + }}); + node.setBounds(new Rectangle2D.Double(0, 0, 32, 100)); + +// Rectangle2D bounds = (Rectangle2D)e.getHint(ElementHints.KEY_BOUNDS); +// if(bounds != null) node.setBounds(bounds); +// String tooltip = e.getHint(KEY_TOOLTIP_TEXT); + Range range = e.getHint(KEY_SLIDER_RANGE); + Double value = e.getHint(KEY_SLIDER_VALUE); + + node.setMinimum((int)Math.round(range.getMin())); // FIXME + node.setMaximum((int)Math.round(range.getMax())); + node.setValue((int)Math.round(value)); + node.setMajorTickSpacing(10); + node.setMinorTickSpacing(5); + node.setPaintLabels(true); + node.setOrientation(JSlider.VERTICAL); + } + update(e); + } + + public void update(IElement e) { + Double value = e.getHint(KEY_SLIDER_VALUE); + + SliderNode node = (SliderNode) e.getHint(KEY_SG_NODE); + if (node != null && value != null) { + node.setValue((int)Math.round(value)); + } + } + + @Override + public void cleanup(IElement e) { + SliderNode node = (SliderNode)e.removeHint(KEY_SG_NODE); + if (node != null) + node.remove(); + } + + @Override + public Rectangle2D getBounds(IElement e, Rectangle2D size) { + Rectangle2D shape = new Rectangle2D.Double(0, 0, 0, 0); + + SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE); + if(node != null && node.getBounds() != null) { + shape = node.getBounds().getBounds2D(); + } + + if(size != null) size.setRect(shape); + return shape; + } + + @Override + public Shape getElementShape(IElement e) { + Shape shape = new Rectangle2D.Double(0, 0, 0, 0); + + SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE); + if(node != null && node.getBounds() != null) { + shape = node.getBounds(); + } + + return shape; + } + + } + + public static class Transformer implements Transform, Move, Rotate, Scale { + + private static final long serialVersionUID = -3704887325602085677L; + + public static final Transformer INSTANCE = new Transformer(null); + + Double aspectRatio; + + public Transformer() { + this(null); + } + + public Transformer(Double aspectRatio) { + this.aspectRatio = aspectRatio; + } + + @Override + public Double getFixedAspectRatio(IElement e) { + return aspectRatio; + } + + @Override + public Point2D getScale(IElement e) { + AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); + return _getScale(at); + } + + @Override + public void setScale(IElement e, Point2D newScale) { + // Doesn't work for monitors. + Point2D oldScale = getScale(e); + double sx = newScale.getX() / oldScale.getX(); + double sy = newScale.getY() / oldScale.getY(); + AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); + at = new AffineTransform(at); + at.scale(sx, sy); + e.setHint(ElementHints.KEY_TRANSFORM, at); + } + + @Override + public Point2D getMaximumScale(IElement e) { + return null; + } + + @Override + public Point2D getMinimumScale(IElement e) { + return null; + } + + private static Point2D _getScale(AffineTransform at) { + double m00 = at.getScaleX(); + double m11 = at.getScaleY(); + double m10 = at.getShearY(); + double m01 = at.getShearX(); + // Project unit vector to canvas + double sx = Math.sqrt(m00 * m00 + m10 * m10); + double sy = Math.sqrt(m01 * m01 + m11 * m11); + return new Point2D.Double(sx, sy); + } + + @Override + public void rotate(IElement e, double theta, Point2D origin) { + if (Double.isNaN(theta)) return; + theta = Math.toDegrees(theta); + Double angle = e.getHint(KEY_DIRECTION); + double newAngle = angle != null ? angle+theta : theta; + newAngle = Math.IEEEremainder(newAngle, 360.0); + e.setHint(KEY_DIRECTION, newAngle); + } + + @Override + public double getAngle(IElement e) { + Double angle = e.getHint(KEY_DIRECTION); + return angle != null ? Math.toRadians(angle) : 0; + } + + @Override + public Point2D getPosition(IElement e) { + AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); + Point2D p = new Point2D.Double(at.getTranslateX(), at.getTranslateY()); + return p; + } + + @Override + public void moveTo(IElement e, double x, double y) { + AffineTransform origAt = e.getHint(ElementHints.KEY_TRANSFORM); + double oldX = origAt.getTranslateX(); + double oldY = origAt.getTranslateY(); + AffineTransform move = AffineTransform.getTranslateInstance(x-oldX, y-oldY); + AffineTransform at2 = new AffineTransform(origAt); + at2.preConcatenate(move); + e.setHint(ElementHints.KEY_TRANSFORM, at2); + } + + @Override + public AffineTransform getTransform(IElement e) { + AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM); + + IElement parentElement = e.getHint(ElementHints.KEY_PARENT_ELEMENT); + if (parentElement == null) + return at; + + Transform parentTransform = parentElement.getElementClass().getSingleItem(Transform.class); + assert(parentTransform!=null); + + AffineTransform result = (AffineTransform)at.clone(); + result.preConcatenate(parentTransform.getTransform(parentElement)); + + return result; + } + + @Override + public void setTransform(IElement e, AffineTransform at) { + e.setHint(ElementHints.KEY_TRANSFORM, at.clone()); + } + } + + static double getOrientationDelta(IElement e, AffineTransform tr) { + Double angle = e.getHint(KEY_DIRECTION); + if (angle == null || Double.isNaN(angle)) + return Double.NaN; + double angrad = Math.toRadians(angle); + + Vector2D forcedAxis = new Vector2D(Math.cos(angrad), Math.sin(angrad)); + Vector2D x = new Vector2D(tr.getScaleX(), tr.getShearX()).normalize(); + double cosa = forcedAxis.dotProduct(x); + double delta = Math.acos(cosa); + return delta; + } + + static class SliderImageFactory implements IFactory { + private double staticScaleX = 1, staticScaleY = 1; + + public SliderImageFactory(double staticScaleX, double staticScaleY) { + this.staticScaleX = staticScaleX; + this.staticScaleY = staticScaleY; + } + + @Override + public Image get() throws ProvisionException { + return new AbstractImage() { + Shape path = new Rectangle2D.Double(0, 0, 50*staticScaleX, 22*staticScaleY); + + @Override + public Rectangle2D getBounds() { + return path.getBounds2D(); + } + + @Override + public EnumSet getFeatures() { + return EnumSet.of(Feature.Vector); + } + + @Override + public Shape getOutline() { + return path; + } + + @Override + public Node init(G2DParentNode parent) { + SliderNode node = parent.getOrCreateNode(""+hashCode(), SliderNode.class); + node.setValue(0); + node.setBounds(new Rectangle2D.Double(0, 0, 50, 22)); + node.setTransform(AffineTransform.getScaleInstance(staticScaleX, staticScaleY)); + return node; + } + }; + } + } + + static final IProvider SLIDER_IMAGE = + ProviderUtils.reference( + ProviderUtils.cache( + ProviderUtils.rasterize( + new SliderImageFactory(0.5, 0.5) + ))); + + static final StaticSymbol SLIDER_SYMBOL = new StaticSymbolImpl( SLIDER_IMAGE.get() ); + + public static final ElementClass SLIDER_CLASS = + ElementClass.compile( + SliderHandlerImpl.INSTANCE, + Transformer.INSTANCE, + BorderColorImpl.BLACK, + SliderSGNode.INSTANCE, + TextImpl.INSTANCE, + TextEditorImpl.INSTANCE, + TextFontImpl.DEFAULT, + TextColorImpl.BLACK, + SimpleElementLayers.INSTANCE, + SLIDER_SYMBOL + ); + + // staticScale{X,Y} define the scale of the static monitor image + public static ElementClass create(IElement parentElement, Range range, Double value, Object component, String suffix, double staticScaleX, double staticScaleY, ElementHandler... extraHandlers) { + // Bit of a hack to be able to define the scale + IProvider staticSliderSymbolProvider = ProviderUtils.reference( + ProviderUtils.cache( + ProviderUtils + .rasterize( + new SliderImageFactory(staticScaleX, staticScaleY)))); + StaticSymbol staticSliderSymbol = new StaticSymbolImpl( staticSliderSymbolProvider.get() ); + return ElementClass.compile( + new Initializer(parentElement, range, value, component, suffix, parentElement != null ? false : true), + SliderHandlerImpl.INSTANCE, + Transformer.INSTANCE, + BorderColorImpl.BLACK, + SliderSGNode.INSTANCE, + TextImpl.INSTANCE, + TextEditorImpl.INSTANCE, + TextFontImpl.DEFAULT, + TextColorImpl.BLACK, + SimpleElementLayers.INSTANCE, + staticSliderSymbol + ).newClassWith(extraHandlers); + } + +}