1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.elementclass.slider;
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;
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;
39 * TODO set Track Rectangle
40 * @author Toni Kalajainen
42 public class SliderHandle extends AbstractGrabbable implements SceneGraph {
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();
52 public Key positionKey = KEY_SLIDER_POSITION;
54 public static final Key SG_NODE = new SceneGraphNodeKey(Node.class, "SUB_SG_NODE");
56 public SliderHandle() {
59 private final static Key KEY_HANDLE_GRAB_POS = new KeyOf(Double.class);
62 public void cleanup(IElement e) {
63 Node node = e.removeHint(SG_NODE);
69 public void init(IElement e, G2DParentNode parent) {
70 CustomSliderNode node = (CustomSliderNode) e.getHint(SG_NODE);
72 node = parent.addNode(CustomSliderNode.class);
73 e.setHint(SG_NODE, node);
76 SliderColorProfile colors = e.getHint(KEY_SLIDER_COLOR_PROFILE);
77 Rectangle2D rect = getBounds(e);
78 boolean enabled = isEnabled(e);
80 double handleWidth = getHandleWidth(e);
81 double handleOffset = getHandleOffset(e);
83 // FIXME: handleOffset is probably never updated..
84 node.init(rect, enabled, colors, handleWidth, handleOffset);
87 public static class CustomSliderNode extends G2DNode {
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;
99 public Rectangle2D getBoundsInLocal() {
103 public void init(Rectangle2D rect, boolean enabled, SliderColorProfile colors, double handleWidth, double handleOffset) {
105 this.enabled = enabled;
106 this.colors = colors;
107 this.handleWidth = handleWidth;
108 this.handleOffset = handleOffset;
112 public void render(Graphics2D g) {
113 double height = rect.getHeight();
114 Rectangle2D r = new Rectangle2D.Double();
115 Line2D l = new Line2D.Double();
119 g.translate(rect.getMinX(), rect.getMinY());
120 g.translate(handleOffset, 0);
122 g.setColor((enabled?colors.HANDLE4:colors.DISABLED_HANDLE4));
123 r.setFrame(1, 1, handleWidth-3, height-3);
126 g.setColor((enabled?colors.HANDLE3:colors.DISABLED_HANDLE3));
127 l.setLine(2, 1, handleWidth-3, 1);
129 l.setLine(1, 2, 1, height-3);
132 g.setColor((enabled?colors.HANDLE5:colors.DISABLED_HANDLE5));
133 l.setLine(2, height-2, handleWidth-3, height-2);
135 l.setLine(handleWidth-2, 2, handleWidth-2, height-3);
138 g.setColor((enabled?colors.HANDLE2:colors.DISABLED_HANDLE2));
139 Path2D p = new Path2D.Double();
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);
152 // Paint scratches (lines) on the handle
153 if (handleWidth>height)
155 g.translate((handleWidth-height)/2, 0);
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));
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));
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));
176 protected boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos) {
177 // 1. Must be enabled
178 if (!isEnabled(e)) return false;
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;
185 double x = mouseElementPos.getX() - bounds.getMinX();
186 double y = mouseElementPos.getY() - bounds.getMinY();
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;
194 // 3. Only one pointer may grab
195 if (getGrabCount(e, ctx)>1) return false;
197 // Everything checks --> OK
202 protected void onDrag(GrabInfo gi, ICanvasContext ctx) {
204 DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
205 Rectangle2D bounds = getBounds(e);
206 double grabPosOnHandle = dp.getElementHint(gi.e, KEY_HANDLE_GRAB_POS);
209 double trackWidth = getTrackWidth(e);
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;
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);
226 * Handle click on track
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;
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();
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;
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);
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);
271 protected void onGrabCancel(GrabInfo gi, ICanvasContext ctx) {
272 DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
273 dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);
277 protected void onRelease(GrabInfo gi, ICanvasContext ctx) {
278 DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
279 dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);
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);
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);
297 public double getPosition(IElement e)
299 Double d = e.getHint(positionKey);
300 if (d==null) return 0.0;
304 private double getHandleOffset(IElement e)
306 double position = getPosition(e);
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);
313 return _calcHandleOffset(width, handleWidth, position, min, max);
316 protected double getHandleWidth(IElement e)
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);
324 private double getTrackWidth(IElement e)
326 return getBounds(e).getWidth();
330 * Calculates the offset of the handle in element coordinates
331 * @return offset of the handle in element coordinates
333 private static double _calcHandleOffset(double trackLength, double handleLength, double position, double min, double max)
335 double varaa = trackLength - handleLength+1;
336 double relativePos = ((position-min))/(max-min);
337 return varaa * relativePos;
341 * Calculate the length of the handle
343 private static double _calcHandleLength(double width, double min, double max)
345 double len = width / ((max-min)+1);
346 if (len<28) len = 28;
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);
356 protected Rectangle2D getBounds(IElement e)
358 return ElementUtils.getElementBounds(e);