]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - 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
diff --git a/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/slider/SliderHandle.java b/bundles/org.simantics.g2d/src/org/simantics/g2d/elementclass/slider/SliderHandle.java
new file mode 100644 (file)
index 0000000..7b0d367
--- /dev/null
@@ -0,0 +1,361 @@
+/*******************************************************************************\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