]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
index 7b0d36775836a7389d8e06c97bfdf2a0483961ec..18a36902b17f869045c7b5e3a29220f2f82309c9 100644 (file)
-/*******************************************************************************\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);
+    }
+
+}