]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/slider/SliderHandle.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / elementclass / slider / SliderHandle.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.g2d.elementclass.slider;
13
14 import java.awt.Graphics2D;
15 import java.awt.geom.Line2D;
16 import java.awt.geom.Path2D;
17 import java.awt.geom.Point2D;
18 import java.awt.geom.Rectangle2D;
19
20 import org.simantics.g2d.canvas.ICanvasContext;
21 import org.simantics.g2d.diagram.participant.DiagramParticipant;
22 import org.simantics.g2d.element.ElementHints;
23 import org.simantics.g2d.element.ElementUtils;
24 import org.simantics.g2d.element.IElement;
25 import org.simantics.g2d.element.SceneGraphNodeKey;
26 import org.simantics.g2d.element.handler.SceneGraph;
27 import org.simantics.g2d.element.handler.Stateful;
28 import org.simantics.g2d.element.handler.impl.AbstractGrabbable;
29 import org.simantics.scenegraph.Node;
30 import org.simantics.scenegraph.g2d.G2DNode;
31 import org.simantics.scenegraph.g2d.G2DParentNode;
32 import org.simantics.scenegraph.g2d.events.MouseEvent;
33 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
34 import org.simantics.utils.datastructures.hints.IHintContext.Key;
35 import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
36
37 /**
38  * 
39  * TODO set Track Rectangle
40  * @author Toni Kalajainen
41  */
42 public class SliderHandle extends AbstractGrabbable implements SceneGraph {
43
44     private static final long serialVersionUID = 3632511991491704966L;
45     public static final Key KEY_SLIDER_COLOR_PROFILE = new KeyOf(SliderColorProfile.class);
46     /** Grab position of handle in terms of element coordinates */
47     public static final Key KEY_SLIDER_POSITION = ElementHints.KEY_VALUE;
48     public static final Key KEY_SLIDER_MIN_VALUE = ElementHints.KEY_MIN_VALUE;
49     public static final Key KEY_SLIDER_MAX_VALUE = ElementHints.KEY_MAX_VALUE;
50     public static final SliderHandle INSTANCE = new SliderHandle();
51
52     public Key positionKey = KEY_SLIDER_POSITION;
53
54     public static final Key SG_NODE = new SceneGraphNodeKey(Node.class, "SUB_SG_NODE");
55
56     public SliderHandle() {
57         super(1000.0);
58     }
59     private final static Key KEY_HANDLE_GRAB_POS = new KeyOf(Double.class);
60
61     @Override
62     public void cleanup(IElement e) {
63         Node node = e.removeHint(SG_NODE);
64         if (node != null)
65             node.remove();
66     }
67
68     @Override
69     public void init(IElement e, G2DParentNode parent) {
70         CustomSliderNode node = (CustomSliderNode) e.getHint(SG_NODE);
71         if (node == null) {
72             node = parent.addNode(CustomSliderNode.class);
73             e.setHint(SG_NODE, node);
74         }
75
76         SliderColorProfile      colors = e.getHint(KEY_SLIDER_COLOR_PROFILE);
77         Rectangle2D             rect = getBounds(e);
78         boolean                         enabled = isEnabled(e);
79
80         double                          handleWidth = getHandleWidth(e);
81         double                          handleOffset = getHandleOffset(e);
82
83         // FIXME: handleOffset is probably never updated..
84         node.init(rect, enabled, colors, handleWidth, handleOffset);
85     }
86
87     public static class CustomSliderNode extends G2DNode {
88         /**
89          * 
90          */
91         private static final long serialVersionUID = 1423400213815428725L;
92         Rectangle2D rect = null;
93         boolean enabled = false;
94         SliderColorProfile colors = null;
95         double handleWidth = 0;
96         double handleOffset = 0;
97
98         @Override
99         public Rectangle2D getBoundsInLocal() {
100             return rect;
101         }
102
103         public void init(Rectangle2D rect, boolean enabled, SliderColorProfile colors, double handleWidth, double handleOffset) {
104             this.rect = rect;
105             this.enabled = enabled;
106             this.colors = colors;
107             this.handleWidth = handleWidth;
108             this.handleOffset = handleOffset;
109         }
110
111         @Override
112         public void render(Graphics2D g) {
113             double                              height = rect.getHeight();
114             Rectangle2D                 r = new Rectangle2D.Double();
115             Line2D                              l = new Line2D.Double();
116
117             height = height + 1;
118
119             g.translate(rect.getMinX(), rect.getMinY());
120             g.translate(handleOffset, 0);
121
122             g.setColor((enabled?colors.HANDLE4:colors.DISABLED_HANDLE4));
123             r.setFrame(1, 1, handleWidth-3, height-3);
124             g.fill(r);
125
126             g.setColor((enabled?colors.HANDLE3:colors.DISABLED_HANDLE3));
127             l.setLine(2, 1, handleWidth-3, 1);
128             g.draw(l);
129             l.setLine(1, 2, 1, height-3);
130             g.draw(l);
131
132             g.setColor((enabled?colors.HANDLE5:colors.DISABLED_HANDLE5));
133             l.setLine(2, height-2, handleWidth-3, height-2);
134             g.draw(l);
135             l.setLine(handleWidth-2, 2, handleWidth-2, height-3);
136             g.draw(l);
137
138             g.setColor((enabled?colors.HANDLE2:colors.DISABLED_HANDLE2));
139             Path2D p = new Path2D.Double();
140             p.moveTo(0, 2);
141             p.lineTo(2, 0);
142             p.lineTo(handleWidth-3, 0);
143             p.lineTo(handleWidth-1, 2);
144             p.lineTo(handleWidth-1, height-3);
145             p.lineTo(handleWidth-3, height-1);
146             p.lineTo(2, height-1);
147             p.lineTo(0, height-3);
148             p.lineTo(0, 2);
149             p.closePath();
150             g.draw(p);
151
152             // Paint scratches (lines) on the handle
153             if (handleWidth>height)
154             {
155                 g.translate((handleWidth-height)/2, 0);
156
157                 g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));
158                 g.drawLine((int) ((height)*0.2), (int) ((height)*0.55)+1, (int) ((height)*0.4), (int) ((height)*0.35)+1);
159                 g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));
160                 g.drawLine((int) ((height)*0.2), (int) ((height)*0.55), (int) ((height)*0.4), (int) ((height)*0.35));
161
162                 g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));
163                 g.drawLine((int) ((height)*0.40), (int) ((height)*0.60)+1, (int) ((height)*0.65), (int) ((height)*0.30)+1);
164                 g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));
165                 g.drawLine((int) ((height)*0.40), (int) ((height)*0.60), (int) ((height)*0.65), (int) ((height)*0.30));
166
167                 g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));
168                 g.drawLine((int) ((height)*0.62), (int) ((height)*0.60)+1, (int) ((height)*0.8), (int) ((height)*0.40)+1);
169                 g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));
170                 g.drawLine((int) ((height)*0.62), (int) ((height)*0.60), (int) ((height)*0.8), (int) ((height)*0.40));
171             }
172         }
173     }
174
175     @Override
176     protected boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos) {
177         // 1. Must be enabled
178         if (!isEnabled(e)) return false;
179
180         // 2. Grab must hit the handle
181         Point2D mouseElementPos = ElementUtils.controlToElementCoordinate(e, ctx, pickPos, null);
182         Rectangle2D bounds = getBounds(e);
183         if (!bounds.contains(mouseElementPos)) return false;
184
185         double x = mouseElementPos.getX() - bounds.getMinX();
186         double y = mouseElementPos.getY() - bounds.getMinY();
187
188         double handleOffset = getHandleOffset(e);
189         double handleWidth      = getHandleWidth(e);
190         boolean pointerOnHandle = (x>=handleOffset && x<=handleOffset+handleWidth);
191         //boolean pointerInBeginning = x < handleOffset;
192         if (!pointerOnHandle) return false;
193
194         // 3. Only one pointer may grab
195         if (getGrabCount(e, ctx)>1) return false;
196
197         // Everything checks --> OK
198         return true;
199     }
200
201     @Override
202     protected void onDrag(GrabInfo gi, ICanvasContext ctx) {
203         IElement e = gi.e;
204         DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
205         Rectangle2D bounds = getBounds(e);
206         double grabPosOnHandle = dp.getElementHint(gi.e, KEY_HANDLE_GRAB_POS);
207
208         // Get track length
209         double trackWidth = getTrackWidth(e);
210         // Get handle legnth
211         double handleWidth = getHandleWidth(e);
212         // Free space on the track == track - handle
213         double varaa = trackWidth - handleWidth+1;
214         // Where are we suggesting where the handle offset should be? (widget coordinates)
215         double suggestedHandlePos = gi.dragPosElement.getX()-grabPosOnHandle;
216         // widget coordinates -> offset 0..1
217         double suggestedOffset = (suggestedHandlePos) /varaa;
218         // 0..1 -> min..max
219         double min = e.getHint(KEY_SLIDER_MIN_VALUE);
220         double max = e.getHint(KEY_SLIDER_MAX_VALUE);
221         double suggestedPosition = (suggestedOffset * (max-min))+min;
222         setPosition(e, suggestedPosition);
223     }
224
225     /**
226      * Handle click on track
227      */
228     @Override
229     public boolean handleMouseEvent(IElement e, ICanvasContext ctx, MouseEvent me) {
230         boolean superResult = super.handleMouseEvent(e, ctx, me);
231         if (superResult) return superResult;
232         if (!(me instanceof MouseClickEvent)) return false;
233         MouseClickEvent mpe = (MouseClickEvent) me;
234         if (mpe.button != MouseEvent.LEFT_BUTTON) return false;
235
236         // 1. Grab must hit the handle
237         Point2D mouseElementPos = ElementUtils.controlToElementCoordinate(e, ctx, me.controlPosition, null);
238         Rectangle2D rect        = getBounds(e);
239         double  mx                      = mouseElementPos.getX();
240         double  my                      = mouseElementPos.getY();
241         boolean onTrackRect     = rect.contains(mx, my);
242         if (!onTrackRect) return false;
243         mx -= rect.getMinX();
244         my -= rect.getMinY();
245
246         double trackWidth       = getTrackWidth(e);
247         double handleOffset = getHandleOffset(e);
248         double handleWidth      = getHandleWidth(e);
249         boolean pointerOnHandle = (mx>=handleOffset && mx<=handleOffset+handleWidth);
250         boolean pointerInBeginning = mx < handleOffset;
251         if (pointerOnHandle) return false;
252
253         double min = e.getHint(KEY_SLIDER_MIN_VALUE);
254         double max = e.getHint(KEY_SLIDER_MAX_VALUE);
255         double pageIncrement = (max-min) / (trackWidth/handleWidth);
256         if (!pointerInBeginning) pageIncrement *= -1;
257         modifyPosition(e, -pageIncrement);
258
259         return true;
260     }
261
262     @Override
263     protected void onGrab(GrabInfo gi, ICanvasContext ctx) {
264         double handlePos = getHandleOffset(gi.e);
265         double grabPosOnHandle = gi.grabPosElement.getX() - handlePos;
266         DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
267         dp.setElementHint(gi.e, KEY_HANDLE_GRAB_POS, grabPosOnHandle);
268     }
269
270     @Override
271     protected void onGrabCancel(GrabInfo gi, ICanvasContext ctx) {
272         DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
273         dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);
274     }
275
276     @Override
277     protected void onRelease(GrabInfo gi, ICanvasContext ctx) {
278         DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
279         dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);
280     }
281
282     public synchronized void modifyPosition(IElement e, double modification) {
283         Double                          position = e.getHint(positionKey);
284         if (position==null) position = 0.0;
285         double newPosition = position + modification;
286         setPosition(e, newPosition);
287     }
288
289     public synchronized void setPosition(IElement e, double position) {
290         double min = e.getHint(KEY_SLIDER_MIN_VALUE);
291         double max = e.getHint(KEY_SLIDER_MAX_VALUE);
292         if (position<min) position = min;
293         if (position>max) position = max;
294         e.setHint(positionKey, position);
295     }
296
297     public double getPosition(IElement e)
298     {
299         Double d = e.getHint(positionKey);
300         if (d==null) return 0.0;
301         return d;
302     }
303
304     private double getHandleOffset(IElement e)
305     {
306         double position = getPosition(e);
307
308         double min = e.getHint(KEY_SLIDER_MIN_VALUE);
309         double max = e.getHint(KEY_SLIDER_MAX_VALUE);
310         double width = getTrackWidth(e);
311         double handleWidth = _calcHandleLength(width, min, max);
312
313         return _calcHandleOffset(width, handleWidth, position, min, max);
314     }
315
316     protected double getHandleWidth(IElement e)
317     {
318         double min = e.getHint(KEY_SLIDER_MIN_VALUE);
319         double max = e.getHint(KEY_SLIDER_MAX_VALUE);
320         double width = getTrackWidth(e);
321         return _calcHandleLength(width, min, max);
322     }
323
324     private double getTrackWidth(IElement e)
325     {
326         return getBounds(e).getWidth();
327     }
328
329     /**
330      * Calculates the offset of the handle in element coordinates
331      * @return offset of the handle in element coordinates
332      */
333     private static double _calcHandleOffset(double trackLength, double handleLength, double position, double min, double max)
334     {
335         double varaa = trackLength - handleLength+1;
336         double relativePos = ((position-min))/(max-min);
337         return varaa * relativePos;
338     }
339
340     /**
341      * Calculate the length of the handle
342      */
343     private static double _calcHandleLength(double width, double min, double max)
344     {
345         double len = width / ((max-min)+1);
346         if (len<28) len = 28;
347         return len;
348     }
349
350     public boolean isEnabled(IElement e) {
351         Stateful enabled = e.getElementClass().getAtMostOneItemOfClass(Stateful.class);
352         if (enabled==null) return true;
353         return enabled.isEnabled(e);
354     }
355
356     protected Rectangle2D getBounds(IElement e)
357     {
358         return ElementUtils.getElementBounds(e);
359     }
360
361 }