]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/diagram/SliderClass.java
03edf6577963485a998a01c5b8409847b1beecca
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / diagram / SliderClass.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.modeling.ui.diagram;
13
14 import java.awt.Graphics2D;
15 import java.awt.Shape;
16 import java.awt.event.ActionEvent;
17 import java.awt.event.ActionListener;
18 import java.awt.geom.AffineTransform;
19 import java.awt.geom.Point2D;
20 import java.awt.geom.Rectangle2D;
21 import java.util.Collection;
22 import java.util.EnumSet;
23
24 import javax.swing.JSlider;
25 import javax.vecmath.Vector2d;
26
27 import org.simantics.g2d.diagram.IDiagram;
28 import org.simantics.g2d.element.ElementClass;
29 import org.simantics.g2d.element.ElementHints;
30 import org.simantics.g2d.element.ElementUtils;
31 import org.simantics.g2d.element.IElement;
32 import org.simantics.g2d.element.SceneGraphNodeKey;
33 import org.simantics.g2d.element.handler.ElementHandler;
34 import org.simantics.g2d.element.handler.InternalSize;
35 import org.simantics.g2d.element.handler.LifeCycle;
36 import org.simantics.g2d.element.handler.Move;
37 import org.simantics.g2d.element.handler.Outline;
38 import org.simantics.g2d.element.handler.Rotate;
39 import org.simantics.g2d.element.handler.Scale;
40 import org.simantics.g2d.element.handler.SceneGraph;
41 import org.simantics.g2d.element.handler.StaticSymbol;
42 import org.simantics.g2d.element.handler.TextEditor;
43 import org.simantics.g2d.element.handler.Transform;
44 import org.simantics.g2d.element.handler.TextEditor.Modifier;
45 import org.simantics.g2d.element.handler.impl.BorderColorImpl;
46 import org.simantics.g2d.element.handler.impl.SimpleElementLayers;
47 import org.simantics.g2d.element.handler.impl.StaticSymbolImpl;
48 import org.simantics.g2d.element.handler.impl.TextColorImpl;
49 import org.simantics.g2d.element.handler.impl.TextEditorImpl;
50 import org.simantics.g2d.element.handler.impl.TextFontImpl;
51 import org.simantics.g2d.element.handler.impl.TextImpl;
52 import org.simantics.g2d.elementclass.MonitorHandler;
53 import org.simantics.g2d.image.Image;
54 import org.simantics.g2d.image.ProviderUtils;
55 import org.simantics.g2d.image.impl.AbstractImage;
56 import org.simantics.scenegraph.Node;
57 import org.simantics.scenegraph.g2d.G2DParentNode;
58 import org.simantics.scenegraph.swing.SliderNode;
59 import org.simantics.utils.datastructures.cache.IFactory;
60 import org.simantics.utils.datastructures.cache.IProvider;
61 import org.simantics.utils.datastructures.cache.ProvisionException;
62 import org.simantics.utils.datastructures.hints.IHintContext.Key;
63 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
64 import org.simantics.utils.strings.format.MetricsFormat;
65 import org.simantics.utils.strings.format.MetricsFormatList;
66
67 /**
68  * @author J-P Laine
69  */
70 public class SliderClass {
71     public static final Key  KEY_SLIDER_RESOURCE_PATH = new KeyOf(Collection.class, "SLIDER_RESOURCE_PATH");
72     public static final Key  KEY_SLIDER_COMPONENT     = new KeyOf(Object.class, "SLIDER_COMPONENT");
73     public static final Key  KEY_SLIDER_SUFFIX        = new KeyOf(String.class, "SLIDER_SUFFIX");
74     public static final Key  KEY_SLIDER_RANGE         = new KeyOf(Range.class, "SLIDER_SUBSTITUTIONS");
75     public static final Key  KEY_SLIDER_GC            = new KeyOf(Graphics2D.class, "SLIDER_GC");
76     public static final Key  KEY_SLIDER_VALUE             = new KeyOf(Double.class, "SLIDER_VALUE");
77     public static final Key  KEY_TOOLTIP_TEXT             = new KeyOf(String.class, "TOOLTIP_TEXT");
78     public static final Key  KEY_NUMBER_FORMAT            = new KeyOf(MetricsFormat.class, "NUMBER_FORMAT");
79
80     /**
81      * If this hint is defined, the monitor will force its x-axis to match this
82      * angle. If this hint doesn't exist, the monitor will not force x-axis
83      * orientation.
84      */
85     public static final Key  KEY_DIRECTION             = new KeyOf(Double.class, "SLIDER_DIRECTION");
86
87     public static final Key  KEY_SG_NODE             = new SceneGraphNodeKey(Node.class, "SLIDER_SG_NODE");
88     public final static MetricsFormat DEFAULT_NUMBER_FORMAT  = MetricsFormatList.METRICS_DECIMAL;
89
90     public static class Range<T> {
91         T min;
92         T max;
93         public Range(T min, T max) {
94                 this.min = min;
95                 this.max = max;
96         }
97         
98         public T getMin() {
99                 return min;
100         }
101         
102         public T getMax() {
103                 return max;
104         }
105     }
106     
107     static MetricsFormat getNumberFormat(IElement e) {
108         return ElementUtils.getHintOrDefault(e, KEY_NUMBER_FORMAT, DEFAULT_NUMBER_FORMAT);
109     }
110
111     static void setNumberFormat(IElement e, MetricsFormat f) {
112         ElementUtils.setOrRemoveHint(e, KEY_NUMBER_FORMAT, f);
113     }
114
115     public static class SliderHandlerImpl implements MonitorHandler {
116         private static final long          serialVersionUID = -4258875745321808416L;
117         public static final MonitorHandler INSTANCE         = new SliderHandlerImpl();
118     }
119
120     static class Initializer implements LifeCycle {
121         private static final long serialVersionUID = 4404942036933073584L;
122
123         IElement parentElement;
124         Range<Double> range;
125         Object component;
126         String suffix;
127         Double value;
128         Boolean hack;
129
130         Initializer(IElement parentElement, Range<Double> range, Double value, Object component, String suffix, Boolean hack) {
131             this.parentElement = parentElement;
132             this.range = range;
133             this.component = component;
134             this.suffix = suffix;
135             this.value = value;
136             this.hack = hack;
137         }
138
139         @Override
140         public void onElementActivated(IDiagram d, IElement e) {
141             if(!hack) {
142                 hack = true;
143                 Point2D parentPos = ElementUtils.getPos(parentElement);
144                 Point2D thisPos = ElementUtils.getPos(e);
145                 Move move = e.getElementClass().getSingleItem(Move.class);
146                 move.moveTo(e, thisPos.getX() - parentPos.getX(), thisPos.getY() - parentPos.getY());
147             }
148         }
149         
150         @Override
151         public void onElementCreated(IElement e) {
152             if(parentElement != null) e.setHint(ElementHints.KEY_PARENT_ELEMENT, parentElement);
153             if(range != null) e.setHint(KEY_SLIDER_RANGE, range);
154             if(component != null) e.setHint(KEY_SLIDER_COMPONENT, component);
155             if(suffix != null) e.setHint(KEY_SLIDER_SUFFIX, suffix);
156             if(value != null) e.setHint(KEY_SLIDER_VALUE, value);
157
158             e.setHint(KEY_DIRECTION, 0.0);
159             e.setHint(KEY_NUMBER_FORMAT, DEFAULT_NUMBER_FORMAT);
160         }
161         @Override
162         public void onElementDeactivated(IDiagram d, IElement e) {
163         }
164         @Override
165         public void onElementDestroyed(IElement e) {
166         }
167     };
168
169     public static void update(IElement e) {
170         SliderSGNode node = e.getElementClass().getAtMostOneItemOfClass(SliderSGNode.class);
171         node.update(e);
172     }
173
174     public static void cleanup(IElement e) {
175         SliderSGNode node = e.getElementClass().getAtMostOneItemOfClass(SliderSGNode.class);
176         node.cleanup(e);
177     }
178
179     public static class SliderSGNode implements SceneGraph, InternalSize, Outline {
180         private static final long serialVersionUID = -106278359626957687L;
181
182         public static final SliderSGNode INSTANCE = new SliderSGNode();
183
184         @Override
185         public void init(final IElement e, final G2DParentNode parent) {
186             // Create node if it doesn't exist yet
187             SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE);
188             if(node == null || node.getBounds() == null || node.getParent() != parent) {
189                 node = parent.addNode(ElementUtils.generateNodeId(e), SliderNode.class);
190                 e.setHint(KEY_SG_NODE, node);
191                 node.setActionListener(new ActionListener() {
192                     @Override
193                     public void actionPerformed(ActionEvent event) {
194                         TextEditor editor = e.getElementClass().getAtMostOneItemOfClass(TextEditor.class);
195                         if(editor != null) {
196                             Modifier modifier = editor.getModifier(e);
197                             if(modifier != null)
198                                 modifier.modify(e, event.getActionCommand());
199                         }
200                     }});
201                 node.setBounds(new Rectangle2D.Double(0, 0, 32, 100));
202
203 //                Rectangle2D bounds = (Rectangle2D)e.getHint(ElementHints.KEY_BOUNDS);
204 //                if(bounds != null) node.setBounds(bounds);
205 //                String tooltip = e.getHint(KEY_TOOLTIP_TEXT);
206                 Range<Double> range = e.getHint(KEY_SLIDER_RANGE);
207                 Double value = e.getHint(KEY_SLIDER_VALUE);
208                 
209                     node.setMinimum((int)Math.round(range.getMin())); // FIXME
210                     node.setMaximum((int)Math.round(range.getMax())); 
211                     node.setValue((int)Math.round(value));
212                     node.setMajorTickSpacing(10);
213                     node.setMinorTickSpacing(5);
214                     node.setPaintLabels(true);
215                     node.setOrientation(JSlider.VERTICAL);
216             }
217             update(e);
218         }
219
220         public void update(IElement e) {
221             Double value = e.getHint(KEY_SLIDER_VALUE);
222
223             SliderNode node = (SliderNode) e.getHint(KEY_SG_NODE);
224             if (node != null && value != null) {
225                 node.setValue((int)Math.round(value));
226             }
227         }
228
229         @Override
230         public void cleanup(IElement e) {
231                 SliderNode node = (SliderNode)e.removeHint(KEY_SG_NODE);
232             if (node != null)
233                 node.remove();
234         }
235
236         @Override
237         public Rectangle2D getBounds(IElement e, Rectangle2D size) {
238             Rectangle2D shape = new Rectangle2D.Double(0, 0, 0, 0);
239
240             SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE);
241             if(node != null && node.getBounds() != null) {
242                 shape = node.getBounds().getBounds2D();
243             }
244
245             if(size != null) size.setRect(shape);
246             return shape;
247         }
248
249         @Override
250         public Shape getElementShape(IElement e) {
251             Shape shape = new Rectangle2D.Double(0, 0, 0, 0);
252
253             SliderNode node = (SliderNode)e.getHint(KEY_SG_NODE);
254             if(node != null && node.getBounds() != null) {
255                 shape = node.getBounds();
256             }
257
258             return shape;
259         }
260
261     }
262
263     public static class Transformer implements Transform, Move, Rotate, Scale {
264
265         private static final long serialVersionUID = -3704887325602085677L;
266
267         public static final Transformer INSTANCE = new Transformer(null);
268
269         Double aspectRatio;
270
271         public Transformer() {
272             this(null);
273         }
274
275         public Transformer(Double aspectRatio) {
276             this.aspectRatio = aspectRatio;
277         }
278
279         @Override
280         public Double getFixedAspectRatio(IElement e) {
281             return aspectRatio;
282         }
283
284         @Override
285         public Point2D getScale(IElement e) {
286             AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM);
287             return _getScale(at);
288         }
289
290         @Override
291         public void setScale(IElement e, Point2D newScale) {
292             // Doesn't work for monitors.
293             Point2D oldScale = getScale(e);
294             double sx = newScale.getX() / oldScale.getX();
295             double sy = newScale.getY() / oldScale.getY();
296             AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM);
297             at = new AffineTransform(at);
298             at.scale(sx, sy);
299             e.setHint(ElementHints.KEY_TRANSFORM, at);
300         }
301
302         @Override
303         public Point2D getMaximumScale(IElement e) {
304             return null;
305         }
306
307         @Override
308         public Point2D getMinimumScale(IElement e) {
309             return null;
310         }
311
312         private static Point2D _getScale(AffineTransform at) {
313             double m00 = at.getScaleX();
314             double m11 = at.getScaleY();
315             double m10 = at.getShearY();
316             double m01 = at.getShearX();
317             // Project unit vector to canvas
318             double sx = Math.sqrt(m00 * m00 + m10 * m10);
319             double sy = Math.sqrt(m01 * m01 + m11 * m11);
320             return new Point2D.Double(sx, sy);
321         }
322
323         @Override
324         public void rotate(IElement e, double theta, Point2D origin) {
325             if (Double.isNaN(theta)) return;
326             theta = Math.toDegrees(theta);
327             Double angle = e.getHint(KEY_DIRECTION);
328             double newAngle = angle != null ? angle+theta : theta;
329             newAngle = Math.IEEEremainder(newAngle, 360.0);
330             e.setHint(KEY_DIRECTION, newAngle);
331         }
332
333         @Override
334         public double getAngle(IElement e) {
335             Double angle = e.getHint(KEY_DIRECTION);
336             return angle != null ? Math.toRadians(angle) : 0;
337         }
338
339         @Override
340         public Point2D getPosition(IElement e) {
341             AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM);
342             Point2D p = new Point2D.Double(at.getTranslateX(), at.getTranslateY());
343             return p;
344         }
345
346         @Override
347         public void moveTo(IElement e, double x, double y) {
348             AffineTransform origAt = e.getHint(ElementHints.KEY_TRANSFORM);
349             double oldX = origAt.getTranslateX();
350             double oldY = origAt.getTranslateY();
351             AffineTransform move = AffineTransform.getTranslateInstance(x-oldX, y-oldY);
352             AffineTransform at2 = new AffineTransform(origAt);
353             at2.preConcatenate(move);
354             e.setHint(ElementHints.KEY_TRANSFORM, at2);
355         }
356
357         @Override
358         public AffineTransform getTransform(IElement e) {
359             AffineTransform at = e.getHint(ElementHints.KEY_TRANSFORM);
360
361             IElement parentElement = e.getHint(ElementHints.KEY_PARENT_ELEMENT);
362             if (parentElement == null)
363                 return at;
364
365             Transform parentTransform = parentElement.getElementClass().getSingleItem(Transform.class);
366             assert(parentTransform!=null);
367
368             AffineTransform result = (AffineTransform)at.clone();
369             result.preConcatenate(parentTransform.getTransform(parentElement));
370
371             return result;
372         }
373
374         @Override
375         public void setTransform(IElement e, AffineTransform at) {
376             e.setHint(ElementHints.KEY_TRANSFORM, at.clone());
377         }
378     }
379
380     static double getOrientationDelta(IElement e, AffineTransform tr) {
381         Double angle = e.getHint(KEY_DIRECTION);
382         if (angle == null || Double.isNaN(angle))
383             return Double.NaN;
384         double angrad = Math.toRadians(angle);
385
386         Vector2d forcedAxis = new Vector2d(Math.cos(angrad), Math.sin(angrad));
387         Vector2d x = new Vector2d(tr.getScaleX(), tr.getShearX());
388         forcedAxis.normalize();
389         x.normalize();
390         double cosa = forcedAxis.dot(x);
391         double delta = Math.acos(cosa);
392         return delta;
393     }
394
395     static class SliderImageFactory implements IFactory<Image> {
396         private double staticScaleX = 1, staticScaleY = 1;
397
398         public SliderImageFactory(double staticScaleX, double staticScaleY) {
399             this.staticScaleX = staticScaleX;
400             this.staticScaleY = staticScaleY;
401         }
402
403         @Override
404         public Image get() throws ProvisionException {
405             return new AbstractImage() {
406                 Shape path = new Rectangle2D.Double(0, 0, 50*staticScaleX, 22*staticScaleY);
407
408                 @Override
409                 public Rectangle2D getBounds() {
410                     return path.getBounds2D();
411                 }
412
413                 @Override
414                 public EnumSet<Feature> getFeatures() {
415                     return EnumSet.of(Feature.Vector);
416                 }
417
418                 @Override
419                 public Shape getOutline() {
420                     return path;
421                 }
422
423                 @Override
424                 public Node init(G2DParentNode parent) {
425                     SliderNode node = parent.getOrCreateNode(""+hashCode(), SliderNode.class);
426                     node.setValue(0);
427                     node.setBounds(new Rectangle2D.Double(0, 0, 50, 22));
428                     node.setTransform(AffineTransform.getScaleInstance(staticScaleX, staticScaleY));
429                     return node;
430                 }
431             };
432         }
433     }
434
435     static final IProvider<Image> SLIDER_IMAGE =
436         ProviderUtils.reference(
437                 ProviderUtils.cache(
438                         ProviderUtils.rasterize(
439                                 new SliderImageFactory(0.5, 0.5)
440                         )));
441
442     static final StaticSymbol SLIDER_SYMBOL = new StaticSymbolImpl( SLIDER_IMAGE.get() );
443
444     public static final ElementClass SLIDER_CLASS =
445         ElementClass.compile(
446                 SliderHandlerImpl.INSTANCE,
447                 Transformer.INSTANCE,
448                 BorderColorImpl.BLACK,
449                 SliderSGNode.INSTANCE,
450                 TextImpl.INSTANCE,
451                 TextEditorImpl.INSTANCE,
452                 TextFontImpl.DEFAULT,
453                 TextColorImpl.BLACK,
454                 SimpleElementLayers.INSTANCE,
455                 SLIDER_SYMBOL
456         );
457
458     // staticScale{X,Y} define the scale of the static monitor image
459     public static ElementClass create(IElement parentElement, Range<Double> range, Double value, Object component, String suffix, double staticScaleX, double staticScaleY, ElementHandler... extraHandlers) {
460         // Bit of a hack to be able to define the scale
461         IProvider<Image> staticSliderSymbolProvider = ProviderUtils.reference(
462                 ProviderUtils.cache(
463                         ProviderUtils
464                         .rasterize(
465                                 new SliderImageFactory(staticScaleX, staticScaleY))));
466         StaticSymbol staticSliderSymbol = new StaticSymbolImpl( staticSliderSymbolProvider.get() );
467         return ElementClass.compile(
468                 new Initializer(parentElement, range, value, component, suffix, parentElement != null ? false : true),
469                 SliderHandlerImpl.INSTANCE,
470                 Transformer.INSTANCE,
471                 BorderColorImpl.BLACK,
472                 SliderSGNode.INSTANCE,
473                 TextImpl.INSTANCE,
474                 TextEditorImpl.INSTANCE,
475                 TextFontImpl.DEFAULT,
476                 TextColorImpl.BLACK,
477                 SimpleElementLayers.INSTANCE,
478                 staticSliderSymbol
479         ).newClassWith(extraHandlers);
480     }
481
482 }