-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.g2d.elementclass.slider;\r
-\r
-import java.awt.Graphics2D;\r
-import java.awt.geom.Line2D;\r
-import java.awt.geom.Path2D;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-\r
-import org.simantics.g2d.canvas.ICanvasContext;\r
-import org.simantics.g2d.diagram.participant.DiagramParticipant;\r
-import org.simantics.g2d.element.ElementHints;\r
-import org.simantics.g2d.element.ElementUtils;\r
-import org.simantics.g2d.element.IElement;\r
-import org.simantics.g2d.element.SceneGraphNodeKey;\r
-import org.simantics.g2d.element.handler.SceneGraph;\r
-import org.simantics.g2d.element.handler.Stateful;\r
-import org.simantics.g2d.element.handler.impl.AbstractGrabbable;\r
-import org.simantics.scenegraph.Node;\r
-import org.simantics.scenegraph.g2d.G2DNode;\r
-import org.simantics.scenegraph.g2d.G2DParentNode;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
-import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
-\r
-/**\r
- * \r
- * TODO set Track Rectangle\r
- * @author Toni Kalajainen\r
- */\r
-public class SliderHandle extends AbstractGrabbable implements SceneGraph {\r
-\r
- private static final long serialVersionUID = 3632511991491704966L;\r
- public static final Key KEY_SLIDER_COLOR_PROFILE = new KeyOf(SliderColorProfile.class);\r
- /** Grab position of handle in terms of element coordinates */\r
- public static final Key KEY_SLIDER_POSITION = ElementHints.KEY_VALUE;\r
- public static final Key KEY_SLIDER_MIN_VALUE = ElementHints.KEY_MIN_VALUE;\r
- public static final Key KEY_SLIDER_MAX_VALUE = ElementHints.KEY_MAX_VALUE;\r
- public static final SliderHandle INSTANCE = new SliderHandle();\r
-\r
- public Key positionKey = KEY_SLIDER_POSITION;\r
-\r
- public static final Key SG_NODE = new SceneGraphNodeKey(Node.class, "SUB_SG_NODE");\r
-\r
- public SliderHandle() {\r
- super(1000.0);\r
- }\r
- private final static Key KEY_HANDLE_GRAB_POS = new KeyOf(Double.class);\r
-\r
- @Override\r
- public void cleanup(IElement e) {\r
- Node node = e.removeHint(SG_NODE);\r
- if (node != null)\r
- node.remove();\r
- }\r
-\r
- @Override\r
- public void init(IElement e, G2DParentNode parent) {\r
- CustomSliderNode node = (CustomSliderNode) e.getHint(SG_NODE);\r
- if (node == null) {\r
- node = parent.addNode(CustomSliderNode.class);\r
- e.setHint(SG_NODE, node);\r
- }\r
-\r
- SliderColorProfile colors = e.getHint(KEY_SLIDER_COLOR_PROFILE);\r
- Rectangle2D rect = getBounds(e);\r
- boolean enabled = isEnabled(e);\r
-\r
- double handleWidth = getHandleWidth(e);\r
- double handleOffset = getHandleOffset(e);\r
-\r
- // FIXME: handleOffset is probably never updated..\r
- node.init(rect, enabled, colors, handleWidth, handleOffset);\r
- }\r
-\r
- public static class CustomSliderNode extends G2DNode {\r
- /**\r
- * \r
- */\r
- private static final long serialVersionUID = 1423400213815428725L;\r
- Rectangle2D rect = null;\r
- boolean enabled = false;\r
- SliderColorProfile colors = null;\r
- double handleWidth = 0;\r
- double handleOffset = 0;\r
-\r
- @Override\r
- public Rectangle2D getBoundsInLocal() {\r
- return rect;\r
- }\r
-\r
- public void init(Rectangle2D rect, boolean enabled, SliderColorProfile colors, double handleWidth, double handleOffset) {\r
- this.rect = rect;\r
- this.enabled = enabled;\r
- this.colors = colors;\r
- this.handleWidth = handleWidth;\r
- this.handleOffset = handleOffset;\r
- }\r
-\r
- @Override\r
- public void render(Graphics2D g) {\r
- double height = rect.getHeight();\r
- Rectangle2D r = new Rectangle2D.Double();\r
- Line2D l = new Line2D.Double();\r
-\r
- height = height + 1;\r
-\r
- g.translate(rect.getMinX(), rect.getMinY());\r
- g.translate(handleOffset, 0);\r
-\r
- g.setColor((enabled?colors.HANDLE4:colors.DISABLED_HANDLE4));\r
- r.setFrame(1, 1, handleWidth-3, height-3);\r
- g.fill(r);\r
-\r
- g.setColor((enabled?colors.HANDLE3:colors.DISABLED_HANDLE3));\r
- l.setLine(2, 1, handleWidth-3, 1);\r
- g.draw(l);\r
- l.setLine(1, 2, 1, height-3);\r
- g.draw(l);\r
-\r
- g.setColor((enabled?colors.HANDLE5:colors.DISABLED_HANDLE5));\r
- l.setLine(2, height-2, handleWidth-3, height-2);\r
- g.draw(l);\r
- l.setLine(handleWidth-2, 2, handleWidth-2, height-3);\r
- g.draw(l);\r
-\r
- g.setColor((enabled?colors.HANDLE2:colors.DISABLED_HANDLE2));\r
- Path2D p = new Path2D.Double();\r
- p.moveTo(0, 2);\r
- p.lineTo(2, 0);\r
- p.lineTo(handleWidth-3, 0);\r
- p.lineTo(handleWidth-1, 2);\r
- p.lineTo(handleWidth-1, height-3);\r
- p.lineTo(handleWidth-3, height-1);\r
- p.lineTo(2, height-1);\r
- p.lineTo(0, height-3);\r
- p.lineTo(0, 2);\r
- p.closePath();\r
- g.draw(p);\r
-\r
- // Paint scratches (lines) on the handle\r
- if (handleWidth>height)\r
- {\r
- g.translate((handleWidth-height)/2, 0);\r
-\r
- g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));\r
- g.drawLine((int) ((height)*0.2), (int) ((height)*0.55)+1, (int) ((height)*0.4), (int) ((height)*0.35)+1);\r
- g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));\r
- g.drawLine((int) ((height)*0.2), (int) ((height)*0.55), (int) ((height)*0.4), (int) ((height)*0.35));\r
-\r
- g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));\r
- g.drawLine((int) ((height)*0.40), (int) ((height)*0.60)+1, (int) ((height)*0.65), (int) ((height)*0.30)+1);\r
- g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));\r
- g.drawLine((int) ((height)*0.40), (int) ((height)*0.60), (int) ((height)*0.65), (int) ((height)*0.30));\r
-\r
- g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));\r
- g.drawLine((int) ((height)*0.62), (int) ((height)*0.60)+1, (int) ((height)*0.8), (int) ((height)*0.40)+1);\r
- g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));\r
- g.drawLine((int) ((height)*0.62), (int) ((height)*0.60), (int) ((height)*0.8), (int) ((height)*0.40));\r
- }\r
- }\r
- }\r
-\r
- @Override\r
- protected boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos) {\r
- // 1. Must be enabled\r
- if (!isEnabled(e)) return false;\r
-\r
- // 2. Grab must hit the handle\r
- Point2D mouseElementPos = ElementUtils.controlToElementCoordinate(e, ctx, pickPos, null);\r
- Rectangle2D bounds = getBounds(e);\r
- if (!bounds.contains(mouseElementPos)) return false;\r
-\r
- double x = mouseElementPos.getX() - bounds.getMinX();\r
- double y = mouseElementPos.getY() - bounds.getMinY();\r
-\r
- double handleOffset = getHandleOffset(e);\r
- double handleWidth = getHandleWidth(e);\r
- boolean pointerOnHandle = (x>=handleOffset && x<=handleOffset+handleWidth);\r
- //boolean pointerInBeginning = x < handleOffset;\r
- if (!pointerOnHandle) return false;\r
-\r
- // 3. Only one pointer may grab\r
- if (getGrabCount(e, ctx)>1) return false;\r
-\r
- // Everything checks --> OK\r
- return true;\r
- }\r
-\r
- @Override\r
- protected void onDrag(GrabInfo gi, ICanvasContext ctx) {\r
- IElement e = gi.e;\r
- DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
- Rectangle2D bounds = getBounds(e);\r
- double grabPosOnHandle = dp.getElementHint(gi.e, KEY_HANDLE_GRAB_POS);\r
-\r
- // Get track length\r
- double trackWidth = getTrackWidth(e);\r
- // Get handle legnth\r
- double handleWidth = getHandleWidth(e);\r
- // Free space on the track == track - handle\r
- double varaa = trackWidth - handleWidth+1;\r
- // Where are we suggesting where the handle offset should be? (widget coordinates)\r
- double suggestedHandlePos = gi.dragPosElement.getX()-grabPosOnHandle;\r
- // widget coordinates -> offset 0..1\r
- double suggestedOffset = (suggestedHandlePos) /varaa;\r
- // 0..1 -> min..max\r
- double min = e.getHint(KEY_SLIDER_MIN_VALUE);\r
- double max = e.getHint(KEY_SLIDER_MAX_VALUE);\r
- double suggestedPosition = (suggestedOffset * (max-min))+min;\r
- setPosition(e, suggestedPosition);\r
- }\r
-\r
- /**\r
- * Handle click on track\r
- */\r
- @Override\r
- public boolean handleMouseEvent(IElement e, ICanvasContext ctx, MouseEvent me) {\r
- boolean superResult = super.handleMouseEvent(e, ctx, me);\r
- if (superResult) return superResult;\r
- if (!(me instanceof MouseClickEvent)) return false;\r
- MouseClickEvent mpe = (MouseClickEvent) me;\r
- if (mpe.button != MouseEvent.LEFT_BUTTON) return false;\r
-\r
- // 1. Grab must hit the handle\r
- Point2D mouseElementPos = ElementUtils.controlToElementCoordinate(e, ctx, me.controlPosition, null);\r
- Rectangle2D rect = getBounds(e);\r
- double mx = mouseElementPos.getX();\r
- double my = mouseElementPos.getY();\r
- boolean onTrackRect = rect.contains(mx, my);\r
- if (!onTrackRect) return false;\r
- mx -= rect.getMinX();\r
- my -= rect.getMinY();\r
-\r
- double trackWidth = getTrackWidth(e);\r
- double handleOffset = getHandleOffset(e);\r
- double handleWidth = getHandleWidth(e);\r
- boolean pointerOnHandle = (mx>=handleOffset && mx<=handleOffset+handleWidth);\r
- boolean pointerInBeginning = mx < handleOffset;\r
- if (pointerOnHandle) return false;\r
-\r
- double min = e.getHint(KEY_SLIDER_MIN_VALUE);\r
- double max = e.getHint(KEY_SLIDER_MAX_VALUE);\r
- double pageIncrement = (max-min) / (trackWidth/handleWidth);\r
- if (!pointerInBeginning) pageIncrement *= -1;\r
- modifyPosition(e, -pageIncrement);\r
-\r
- return true;\r
- }\r
-\r
- @Override\r
- protected void onGrab(GrabInfo gi, ICanvasContext ctx) {\r
- double handlePos = getHandleOffset(gi.e);\r
- double grabPosOnHandle = gi.grabPosElement.getX() - handlePos;\r
- DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
- dp.setElementHint(gi.e, KEY_HANDLE_GRAB_POS, grabPosOnHandle);\r
- }\r
-\r
- @Override\r
- protected void onGrabCancel(GrabInfo gi, ICanvasContext ctx) {\r
- DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
- dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);\r
- }\r
-\r
- @Override\r
- protected void onRelease(GrabInfo gi, ICanvasContext ctx) {\r
- DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
- dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);\r
- }\r
-\r
- public synchronized void modifyPosition(IElement e, double modification) {\r
- Double position = e.getHint(positionKey);\r
- if (position==null) position = 0.0;\r
- double newPosition = position + modification;\r
- setPosition(e, newPosition);\r
- }\r
-\r
- public synchronized void setPosition(IElement e, double position) {\r
- double min = e.getHint(KEY_SLIDER_MIN_VALUE);\r
- double max = e.getHint(KEY_SLIDER_MAX_VALUE);\r
- if (position<min) position = min;\r
- if (position>max) position = max;\r
- e.setHint(positionKey, position);\r
- }\r
-\r
- public double getPosition(IElement e)\r
- {\r
- Double d = e.getHint(positionKey);\r
- if (d==null) return 0.0;\r
- return d;\r
- }\r
-\r
- private double getHandleOffset(IElement e)\r
- {\r
- double position = getPosition(e);\r
-\r
- double min = e.getHint(KEY_SLIDER_MIN_VALUE);\r
- double max = e.getHint(KEY_SLIDER_MAX_VALUE);\r
- double width = getTrackWidth(e);\r
- double handleWidth = _calcHandleLength(width, min, max);\r
-\r
- return _calcHandleOffset(width, handleWidth, position, min, max);\r
- }\r
-\r
- protected double getHandleWidth(IElement e)\r
- {\r
- double min = e.getHint(KEY_SLIDER_MIN_VALUE);\r
- double max = e.getHint(KEY_SLIDER_MAX_VALUE);\r
- double width = getTrackWidth(e);\r
- return _calcHandleLength(width, min, max);\r
- }\r
-\r
- private double getTrackWidth(IElement e)\r
- {\r
- return getBounds(e).getWidth();\r
- }\r
-\r
- /**\r
- * Calculates the offset of the handle in element coordinates\r
- * @return offset of the handle in element coordinates\r
- */\r
- private static double _calcHandleOffset(double trackLength, double handleLength, double position, double min, double max)\r
- {\r
- double varaa = trackLength - handleLength+1;\r
- double relativePos = ((position-min))/(max-min);\r
- return varaa * relativePos;\r
- }\r
-\r
- /**\r
- * Calculate the length of the handle\r
- */\r
- private static double _calcHandleLength(double width, double min, double max)\r
- {\r
- double len = width / ((max-min)+1);\r
- if (len<28) len = 28;\r
- return len;\r
- }\r
-\r
- public boolean isEnabled(IElement e) {\r
- Stateful enabled = e.getElementClass().getAtMostOneItemOfClass(Stateful.class);\r
- if (enabled==null) return true;\r
- return enabled.isEnabled(e);\r
- }\r
-\r
- protected Rectangle2D getBounds(IElement e)\r
- {\r
- return ElementUtils.getElementBounds(e);\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * 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.g2d.elementclass.slider;
+
+import java.awt.Graphics2D;
+import java.awt.geom.Line2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+import org.simantics.g2d.canvas.ICanvasContext;
+import org.simantics.g2d.diagram.participant.DiagramParticipant;
+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.SceneGraph;
+import org.simantics.g2d.element.handler.Stateful;
+import org.simantics.g2d.element.handler.impl.AbstractGrabbable;
+import org.simantics.scenegraph.Node;
+import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.g2d.events.MouseEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;
+
+/**
+ *
+ * TODO set Track Rectangle
+ * @author Toni Kalajainen
+ */
+public class SliderHandle extends AbstractGrabbable implements SceneGraph {
+
+ private static final long serialVersionUID = 3632511991491704966L;
+ public static final Key KEY_SLIDER_COLOR_PROFILE = new KeyOf(SliderColorProfile.class);
+ /** Grab position of handle in terms of element coordinates */
+ public static final Key KEY_SLIDER_POSITION = ElementHints.KEY_VALUE;
+ public static final Key KEY_SLIDER_MIN_VALUE = ElementHints.KEY_MIN_VALUE;
+ public static final Key KEY_SLIDER_MAX_VALUE = ElementHints.KEY_MAX_VALUE;
+ public static final SliderHandle INSTANCE = new SliderHandle();
+
+ public Key positionKey = KEY_SLIDER_POSITION;
+
+ public static final Key SG_NODE = new SceneGraphNodeKey(Node.class, "SUB_SG_NODE");
+
+ public SliderHandle() {
+ super(1000.0);
+ }
+ private final static Key KEY_HANDLE_GRAB_POS = new KeyOf(Double.class);
+
+ @Override
+ public void cleanup(IElement e) {
+ Node node = e.removeHint(SG_NODE);
+ if (node != null)
+ node.remove();
+ }
+
+ @Override
+ public void init(IElement e, G2DParentNode parent) {
+ CustomSliderNode node = (CustomSliderNode) e.getHint(SG_NODE);
+ if (node == null) {
+ node = parent.addNode(CustomSliderNode.class);
+ e.setHint(SG_NODE, node);
+ }
+
+ SliderColorProfile colors = e.getHint(KEY_SLIDER_COLOR_PROFILE);
+ Rectangle2D rect = getBounds(e);
+ boolean enabled = isEnabled(e);
+
+ double handleWidth = getHandleWidth(e);
+ double handleOffset = getHandleOffset(e);
+
+ // FIXME: handleOffset is probably never updated..
+ node.init(rect, enabled, colors, handleWidth, handleOffset);
+ }
+
+ public static class CustomSliderNode extends G2DNode {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1423400213815428725L;
+ Rectangle2D rect = null;
+ boolean enabled = false;
+ SliderColorProfile colors = null;
+ double handleWidth = 0;
+ double handleOffset = 0;
+
+ @Override
+ public Rectangle2D getBoundsInLocal() {
+ return rect;
+ }
+
+ public void init(Rectangle2D rect, boolean enabled, SliderColorProfile colors, double handleWidth, double handleOffset) {
+ this.rect = rect;
+ this.enabled = enabled;
+ this.colors = colors;
+ this.handleWidth = handleWidth;
+ this.handleOffset = handleOffset;
+ }
+
+ @Override
+ public void render(Graphics2D g) {
+ double height = rect.getHeight();
+ Rectangle2D r = new Rectangle2D.Double();
+ Line2D l = new Line2D.Double();
+
+ height = height + 1;
+
+ g.translate(rect.getMinX(), rect.getMinY());
+ g.translate(handleOffset, 0);
+
+ g.setColor((enabled?colors.HANDLE4:colors.DISABLED_HANDLE4));
+ r.setFrame(1, 1, handleWidth-3, height-3);
+ g.fill(r);
+
+ g.setColor((enabled?colors.HANDLE3:colors.DISABLED_HANDLE3));
+ l.setLine(2, 1, handleWidth-3, 1);
+ g.draw(l);
+ l.setLine(1, 2, 1, height-3);
+ g.draw(l);
+
+ g.setColor((enabled?colors.HANDLE5:colors.DISABLED_HANDLE5));
+ l.setLine(2, height-2, handleWidth-3, height-2);
+ g.draw(l);
+ l.setLine(handleWidth-2, 2, handleWidth-2, height-3);
+ g.draw(l);
+
+ g.setColor((enabled?colors.HANDLE2:colors.DISABLED_HANDLE2));
+ Path2D p = new Path2D.Double();
+ p.moveTo(0, 2);
+ p.lineTo(2, 0);
+ p.lineTo(handleWidth-3, 0);
+ p.lineTo(handleWidth-1, 2);
+ p.lineTo(handleWidth-1, height-3);
+ p.lineTo(handleWidth-3, height-1);
+ p.lineTo(2, height-1);
+ p.lineTo(0, height-3);
+ p.lineTo(0, 2);
+ p.closePath();
+ g.draw(p);
+
+ // Paint scratches (lines) on the handle
+ if (handleWidth>height)
+ {
+ g.translate((handleWidth-height)/2, 0);
+
+ g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));
+ g.drawLine((int) ((height)*0.2), (int) ((height)*0.55)+1, (int) ((height)*0.4), (int) ((height)*0.35)+1);
+ g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));
+ g.drawLine((int) ((height)*0.2), (int) ((height)*0.55), (int) ((height)*0.4), (int) ((height)*0.35));
+
+ g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));
+ g.drawLine((int) ((height)*0.40), (int) ((height)*0.60)+1, (int) ((height)*0.65), (int) ((height)*0.30)+1);
+ g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));
+ g.drawLine((int) ((height)*0.40), (int) ((height)*0.60), (int) ((height)*0.65), (int) ((height)*0.30));
+
+ g.setColor((enabled?colors.HANDLE8:colors.DISABLED_HANDLE8));
+ g.drawLine((int) ((height)*0.62), (int) ((height)*0.60)+1, (int) ((height)*0.8), (int) ((height)*0.40)+1);
+ g.setColor((enabled?colors.HANDLE7:colors.DISABLED_HANDLE7));
+ g.drawLine((int) ((height)*0.62), (int) ((height)*0.60), (int) ((height)*0.8), (int) ((height)*0.40));
+ }
+ }
+ }
+
+ @Override
+ protected boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos) {
+ // 1. Must be enabled
+ if (!isEnabled(e)) return false;
+
+ // 2. Grab must hit the handle
+ Point2D mouseElementPos = ElementUtils.controlToElementCoordinate(e, ctx, pickPos, null);
+ Rectangle2D bounds = getBounds(e);
+ if (!bounds.contains(mouseElementPos)) return false;
+
+ double x = mouseElementPos.getX() - bounds.getMinX();
+ double y = mouseElementPos.getY() - bounds.getMinY();
+
+ double handleOffset = getHandleOffset(e);
+ double handleWidth = getHandleWidth(e);
+ boolean pointerOnHandle = (x>=handleOffset && x<=handleOffset+handleWidth);
+ //boolean pointerInBeginning = x < handleOffset;
+ if (!pointerOnHandle) return false;
+
+ // 3. Only one pointer may grab
+ if (getGrabCount(e, ctx)>1) return false;
+
+ // Everything checks --> OK
+ return true;
+ }
+
+ @Override
+ protected void onDrag(GrabInfo gi, ICanvasContext ctx) {
+ IElement e = gi.e;
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
+ Rectangle2D bounds = getBounds(e);
+ double grabPosOnHandle = dp.getElementHint(gi.e, KEY_HANDLE_GRAB_POS);
+
+ // Get track length
+ double trackWidth = getTrackWidth(e);
+ // Get handle legnth
+ double handleWidth = getHandleWidth(e);
+ // Free space on the track == track - handle
+ double varaa = trackWidth - handleWidth+1;
+ // Where are we suggesting where the handle offset should be? (widget coordinates)
+ double suggestedHandlePos = gi.dragPosElement.getX()-grabPosOnHandle;
+ // widget coordinates -> offset 0..1
+ double suggestedOffset = (suggestedHandlePos) /varaa;
+ // 0..1 -> min..max
+ double min = e.getHint(KEY_SLIDER_MIN_VALUE);
+ double max = e.getHint(KEY_SLIDER_MAX_VALUE);
+ double suggestedPosition = (suggestedOffset * (max-min))+min;
+ setPosition(e, suggestedPosition);
+ }
+
+ /**
+ * Handle click on track
+ */
+ @Override
+ public boolean handleMouseEvent(IElement e, ICanvasContext ctx, MouseEvent me) {
+ boolean superResult = super.handleMouseEvent(e, ctx, me);
+ if (superResult) return superResult;
+ if (!(me instanceof MouseClickEvent)) return false;
+ MouseClickEvent mpe = (MouseClickEvent) me;
+ if (mpe.button != MouseEvent.LEFT_BUTTON) return false;
+
+ // 1. Grab must hit the handle
+ Point2D mouseElementPos = ElementUtils.controlToElementCoordinate(e, ctx, me.controlPosition, null);
+ Rectangle2D rect = getBounds(e);
+ double mx = mouseElementPos.getX();
+ double my = mouseElementPos.getY();
+ boolean onTrackRect = rect.contains(mx, my);
+ if (!onTrackRect) return false;
+ mx -= rect.getMinX();
+ my -= rect.getMinY();
+
+ double trackWidth = getTrackWidth(e);
+ double handleOffset = getHandleOffset(e);
+ double handleWidth = getHandleWidth(e);
+ boolean pointerOnHandle = (mx>=handleOffset && mx<=handleOffset+handleWidth);
+ boolean pointerInBeginning = mx < handleOffset;
+ if (pointerOnHandle) return false;
+
+ double min = e.getHint(KEY_SLIDER_MIN_VALUE);
+ double max = e.getHint(KEY_SLIDER_MAX_VALUE);
+ double pageIncrement = (max-min) / (trackWidth/handleWidth);
+ if (!pointerInBeginning) pageIncrement *= -1;
+ modifyPosition(e, -pageIncrement);
+
+ return true;
+ }
+
+ @Override
+ protected void onGrab(GrabInfo gi, ICanvasContext ctx) {
+ double handlePos = getHandleOffset(gi.e);
+ double grabPosOnHandle = gi.grabPosElement.getX() - handlePos;
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
+ dp.setElementHint(gi.e, KEY_HANDLE_GRAB_POS, grabPosOnHandle);
+ }
+
+ @Override
+ protected void onGrabCancel(GrabInfo gi, ICanvasContext ctx) {
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
+ dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);
+ }
+
+ @Override
+ protected void onRelease(GrabInfo gi, ICanvasContext ctx) {
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);
+ dp.removeElementHint(gi.e, KEY_HANDLE_GRAB_POS);
+ }
+
+ public synchronized void modifyPosition(IElement e, double modification) {
+ Double position = e.getHint(positionKey);
+ if (position==null) position = 0.0;
+ double newPosition = position + modification;
+ setPosition(e, newPosition);
+ }
+
+ public synchronized void setPosition(IElement e, double position) {
+ double min = e.getHint(KEY_SLIDER_MIN_VALUE);
+ double max = e.getHint(KEY_SLIDER_MAX_VALUE);
+ if (position<min) position = min;
+ if (position>max) position = max;
+ e.setHint(positionKey, position);
+ }
+
+ public double getPosition(IElement e)
+ {
+ Double d = e.getHint(positionKey);
+ if (d==null) return 0.0;
+ return d;
+ }
+
+ private double getHandleOffset(IElement e)
+ {
+ double position = getPosition(e);
+
+ double min = e.getHint(KEY_SLIDER_MIN_VALUE);
+ double max = e.getHint(KEY_SLIDER_MAX_VALUE);
+ double width = getTrackWidth(e);
+ double handleWidth = _calcHandleLength(width, min, max);
+
+ return _calcHandleOffset(width, handleWidth, position, min, max);
+ }
+
+ protected double getHandleWidth(IElement e)
+ {
+ double min = e.getHint(KEY_SLIDER_MIN_VALUE);
+ double max = e.getHint(KEY_SLIDER_MAX_VALUE);
+ double width = getTrackWidth(e);
+ return _calcHandleLength(width, min, max);
+ }
+
+ private double getTrackWidth(IElement e)
+ {
+ return getBounds(e).getWidth();
+ }
+
+ /**
+ * Calculates the offset of the handle in element coordinates
+ * @return offset of the handle in element coordinates
+ */
+ private static double _calcHandleOffset(double trackLength, double handleLength, double position, double min, double max)
+ {
+ double varaa = trackLength - handleLength+1;
+ double relativePos = ((position-min))/(max-min);
+ return varaa * relativePos;
+ }
+
+ /**
+ * Calculate the length of the handle
+ */
+ private static double _calcHandleLength(double width, double min, double max)
+ {
+ double len = width / ((max-min)+1);
+ if (len<28) len = 28;
+ return len;
+ }
+
+ public boolean isEnabled(IElement e) {
+ Stateful enabled = e.getElementClass().getAtMostOneItemOfClass(Stateful.class);
+ if (enabled==null) return true;
+ return enabled.isEnabled(e);
+ }
+
+ protected Rectangle2D getBounds(IElement e)
+ {
+ return ElementUtils.getElementBounds(e);
+ }
+
+}