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