]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.g2d/src/org/simantics/g2d/participant/MouseUtil.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / participant / MouseUtil.java
index 8ebfb564ab80b0846351c96ea5ce5f43d1452094..44ded472960a1686f100d1cc69f8fed9bd8137a6 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
-/*\r
- *\r
- * @author Toni Kalajainen\r
- */\r
-package org.simantics.g2d.participant;\r
-\r
-import java.awt.geom.Point2D;\r
-import java.util.Collection;\r
-import java.util.HashMap;\r
-import java.util.Map;\r
-import java.util.Map.Entry;\r
-\r
-import org.simantics.g2d.canvas.Hints;\r
-import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
-import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;\r
-import org.simantics.g2d.canvas.impl.HintReflection.HintListener;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent;\r
-import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
-import org.simantics.utils.datastructures.hints.IHintObservable;\r
-import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
-\r
-/**\r
- * MouseUtil tracks position and button status of mice.\r
- * It remembers where mouse was on control and on diagram when each of its button\r
- * was pressed. \r
- * \r
- * MouseUtil generates synthetic mouse events {@link MouseClickEvent} and\r
- * {@link MouseDragBegin} for convenience. \r
- * \r
- * It also tracks distance how far mouse has moved since each of its \r
- * button was pressed (used for click).\r
- * <p>\r
- * There is also debug cursor painter which can be enabled/disabled with \r
- * hint KEY_CURSOR_STATUS.\r
- * \r
- * @TODO From to mouse monitor and mouse painter classes\r
- */\r
-public class MouseUtil extends AbstractCanvasParticipant {     \r
-\r
-    protected @Dependency TransformUtil util;\r
-\r
-    /** Mice info */\r
-    protected  Map<Integer, MouseInfo> miceInfo = new HashMap<Integer, MouseInfo>();\r
-    protected  Map<Integer, MouseInfo> micePressedInfo = new HashMap<Integer, MouseInfo>();\r
-\r
-    public static class MouseClickProfile {\r
-        public static final MouseClickProfile DEFAULT = new MouseClickProfile(); \r
-        /** Maximum time of holding a button down that counts as a click */ \r
-        public long clickHoldTimeTolerance = 300L;\r
-        /** Maximum number of pixels mouse may move while pressed to count a click */ \r
-        public double movementTolerance = 3.5;\r
-        /** Maximum time to wait for clicks to count as consecutive */\r
-        public long consecutiveToleranceTime = 380L;\r
-    }\r
-\r
-    protected MouseClickProfile profile = MouseClickProfile.DEFAULT;\r
-\r
-    @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")\r
-    public void transformChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {\r
-        if (oldValue != null && oldValue.equals(newValue)) return;\r
-        sendMicePos();\r
-    }\r
-\r
-    @EventHandler(priority = Integer.MAX_VALUE)\r
-    public boolean handleMouseEvent(MouseEvent e) \r
-    {\r
-        assertDependencies();\r
-        if (e instanceof MouseEnterEvent)\r
-        {\r
-            Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);\r
-            // Reset mouse\r
-            MouseInfo mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);\r
-            miceInfo.put(e.mouseId, mi);\r
-        } else \r
-            if (e instanceof MouseExitEvent)\r
-            {\r
-                miceInfo.remove(e.mouseId);\r
-            } else\r
-                if (e instanceof MouseMovedEvent)\r
-                {\r
-                    Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);\r
-                    double deltaDistance = 0;\r
-                    MouseInfo mi = miceInfo.get(e.mouseId);\r
-                    if (mi==null) {\r
-                        mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, 0/*e.buttons*/);\r
-                        miceInfo.put(e.mouseId, mi);\r
-                    } else {\r
-                        deltaDistance = e.controlPosition.distance(mi.controlPosition);\r
-                        mi.controlPosition = e.controlPosition;\r
-                        mi.canvasPosition = canvasPosition;\r
-                    }\r
-\r
-                    if (deltaDistance>0)\r
-                        mi.addDistanceForButtons(deltaDistance);\r
-\r
-                    // Send mouse drag events.\r
-                    for (ButtonInfo bi : mi.buttonPressInfo.values())\r
-                    {\r
-                        if (!bi.down) continue;\r
-                        if (bi.deltaMotion<=profile.movementTolerance) continue;\r
-                        if (bi.drag) continue;\r
-                        bi.drag = true;\r
-                        MouseDragBegin db = new MouseDragBegin(\r
-                                this, e.time, e.mouseId, e.buttons, e.stateMask, bi.button,\r
-                                bi.canvasPosition, bi.controlPosition,\r
-                                e.controlPosition, e.screenPosition\r
-                                );\r
-                        getContext().getEventQueue().queueFirst(db);\r
-                    }\r
-\r
-                } else\r
-                    if (e instanceof MouseButtonPressedEvent)\r
-                    {\r
-                        Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);\r
-                        MouseButtonPressedEvent me = (MouseButtonPressedEvent) e;\r
-                        MouseInfo mi = miceInfo.get(e.mouseId);\r
-                        if (mi==null) {\r
-                            mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);\r
-                            miceInfo.put(e.mouseId, mi);\r
-                            micePressedInfo.put(e.mouseId, mi);\r
-                        } else {\r
-                            mi.controlPosition = e.controlPosition;\r
-                            mi.canvasPosition = canvasPosition;\r
-                            micePressedInfo.put(e.mouseId, \r
-                                    new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons));\r
-                        }                      \r
-                        mi.setButtonPressed(me.button, e.stateMask, e.controlPosition, canvasPosition, e.time);\r
-                    } else if (e instanceof MouseButtonReleasedEvent) {\r
-                        MouseButtonReleasedEvent me = (MouseButtonReleasedEvent) e;\r
-                        Point2D canvasPosition = util.controlToCanvas(me.controlPosition, null);\r
-                        MouseInfo mi = miceInfo.get(me.mouseId);\r
-                        if (mi==null) {\r
-                            mi = new MouseInfo(e.mouseId, me.controlPosition, canvasPosition, 0/*me.buttons*/);\r
-                            miceInfo.put(me.mouseId, mi);\r
-                        } else {\r
-                            mi.controlPosition = me.controlPosition;\r
-                            mi.canvasPosition = canvasPosition;\r
-                        }\r
-                        ButtonInfo bi = mi.releaseButton(me.button, me.time);\r
-                        if (bi==null) return false;\r
-\r
-                        if (me.holdTime > profile.clickHoldTimeTolerance) return false;\r
-                        if (bi.deltaMotion>profile.movementTolerance) return false;\r
-                        // This is a click\r
-\r
-                        long timeSinceLastClick = me.time - bi.lastClickEventTime;\r
-                        bi.lastClickEventTime = me.time;\r
-\r
-                        // reset click counter\r
-                        if (timeSinceLastClick>profile.consecutiveToleranceTime) \r
-                            bi.clickCount = 1;\r
-                        else\r
-                            bi.clickCount++;\r
-\r
-                        MouseClickEvent ce = \r
-                                new MouseClickEvent(getContext(), e.time, e.mouseId, e.buttons, e.stateMask, me.button, bi.clickCount, me.controlPosition, me.screenPosition);                         \r
-                        getContext().getEventQueue().queueFirst(ce);\r
-                    }\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * Get last known position of a mouse\r
-     * \r
-     * @param mouseId mouse id\r
-     * @return mouse position in canvas coordinates or null if mouse exited\r
-     *         canvas\r
-     */\r
-    public MouseInfo getMouseInfo(int mouseId) {\r
-        return miceInfo.get(mouseId);\r
-    }\r
-\r
-    public MouseInfo getMousePressedInfo(int mouseId) {\r
-        return micePressedInfo.get(mouseId);\r
-    }\r
-\r
-    /**\r
-     * Get button info\r
-     * @param mouseId mouse id\r
-     * @param buttonId button id\r
-     * @return null if button has never been pressed, or button info\r
-     */\r
-    public ButtonInfo getButtonInfo(int mouseId, int buttonId)\r
-    {\r
-        MouseInfo mi = miceInfo.get(mouseId);\r
-        if (mi==null) return null;\r
-        return mi.getButtonPressInfo(buttonId);\r
-    }\r
-\r
-    /**\r
-     * Get a snapshot of statuses of all mice\r
-     * @return a snapshot\r
-     */\r
-    public Map<Integer, MouseInfo> getMiceInfo()\r
-    {\r
-        return new HashMap<Integer, MouseInfo>(miceInfo);\r
-    }\r
-\r
-    public int getMouseCount()\r
-    {\r
-        return miceInfo.size();\r
-    }\r
-\r
-    public MouseClickProfile getProfile() {\r
-        return profile;\r
-    }\r
-\r
-    public void setProfile(MouseClickProfile profile) {\r
-        assert(profile!=null);\r
-        this.profile = profile;\r
-    }\r
-\r
-    public static final class MouseInfo {\r
-        public int mouseId;\r
-        public Point2D controlPosition;\r
-        public Point2D canvasPosition;\r
-        public int buttons;\r
-        public MouseInfo(int mouseId, Point2D initialControlPos, Point2D initialCanvasPosition, int initialButtonMask) {\r
-            this.mouseId = mouseId;\r
-            this.controlPosition = initialControlPos;\r
-            this.canvasPosition = initialCanvasPosition;\r
-            int buttonId = 0;\r
-            while (initialButtonMask!=0) {\r
-                if ((initialButtonMask & 1)==1)\r
-                    setButtonPressed(buttonId, 0, initialControlPos, initialCanvasPosition, Long.MIN_VALUE);\r
-                initialButtonMask >>>= 1;\r
-                buttonId++;\r
-            }\r
-        }\r
-\r
-        public boolean isMouseButtonPressed(int buttonId)\r
-        {\r
-            ButtonInfo bi = buttonPressInfo.get(buttonId);\r
-            return bi!=null && bi.down;\r
-        }\r
-        private void _countButtonMask() {\r
-            int result = 0;\r
-            for (ButtonInfo pi : buttonPressInfo.values())\r
-            {\r
-                if (!pi.down) continue;\r
-                result |= 1 << (pi.button-1);\r
-            }\r
-            this.buttons = result;\r
-        }\r
-        public  Map<Integer, ButtonInfo> buttonPressInfo = new HashMap<Integer, ButtonInfo>();\r
-        public void setButtonPressed(int buttonId, int stateMask, Point2D controlPos, Point2D canvasPos, long eventTime) {\r
-            ButtonInfo bi = getOrCreateButtonInfo(buttonId); \r
-            bi.canvasPosition = canvasPos;\r
-            bi.controlPosition = controlPos;\r
-            bi.systemTime = System.currentTimeMillis();\r
-            bi.eventTime = eventTime;\r
-            bi.down = true;\r
-            bi.deltaMotion = 0;\r
-            bi.drag = false;\r
-            bi.stateMask = stateMask;\r
-            _countButtonMask();\r
-        }\r
-        public ButtonInfo releaseButton(int buttonId, long eventTime) {\r
-            ButtonInfo bi = getButtonPressInfo(buttonId);\r
-            if (bi==null) return null;\r
-            bi.down = false;\r
-            bi.holdTime = eventTime - bi.eventTime;\r
-            _countButtonMask();\r
-            return bi;\r
-        }\r
-        ButtonInfo getOrCreateButtonInfo(int buttonId)\r
-        {\r
-            ButtonInfo bi = buttonPressInfo.get(buttonId);\r
-            if (bi==null) bi = new ButtonInfo(buttonId);\r
-            buttonPressInfo.put(buttonId, bi);\r
-            return bi;\r
-        }\r
-        public ButtonInfo getButtonPressInfo(int buttonId) {\r
-            return buttonPressInfo.get(buttonId);\r
-        }\r
-        public void addDistanceForButtons(double distance) {\r
-            for (ButtonInfo bi : buttonPressInfo.values())\r
-            {\r
-                if (!bi.down) continue;\r
-                bi.deltaMotion += distance;\r
-            }\r
-        }\r
-        public Collection<ButtonInfo> getButtonInfos() {\r
-            return buttonPressInfo.values();\r
-        }\r
-    }\r
-\r
-    /** Status of mouse's button press */\r
-    public static final class ButtonInfo {\r
-        /** Position on press */\r
-        public Point2D controlPosition;\r
-        /** Position on press */\r
-        public Point2D canvasPosition;\r
-        public final int button;\r
-        public int stateMask;\r
-        /** System time when pressed */\r
-        public long systemTime;\r
-        /** Event time when pressed */\r
-        public long eventTime;\r
-        /** Hold time when released */\r
-        public long holdTime;\r
-        /** Current up / down status */\r
-        public boolean down = false;\r
-        /** Total movement in pixels since press */ \r
-        public double deltaMotion = 0.0;\r
-        /** Click Count */\r
-        public int clickCount = 0;\r
-        /** Time of last click */\r
-        public long lastClickEventTime = Long.MIN_VALUE;\r
-        /** Dragged (set true when possibility for click is excluded) */\r
-        public boolean drag = false;\r
-        public ButtonInfo(int button) {\r
-            this.button = button;\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Sends mice positions as an event\r
-     */\r
-    private void sendMicePos()\r
-    {\r
-        long time = System.currentTimeMillis();\r
-        for (Entry<Integer, MouseInfo> e : miceInfo.entrySet()) {\r
-            MouseInfo mi = e.getValue();\r
-            MouseMovedEvent mme;\r
-            // FIXME: screenPosition as null (requires adding screenPos into MouseInfo)\r
-            mme = new MouseMovedEvent(getContext(), time, e.getKey(), mi.buttons, 0 /* FIXME ??? */, mi.controlPosition, null);\r
-            getContext().getEventQueue().queueEvent(mme);\r
-        }\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
+ *******************************************************************************/
+/*
+ *
+ * @author Toni Kalajainen
+ */
+package org.simantics.g2d.participant;
+
+import java.awt.geom.Point2D;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.simantics.g2d.canvas.Hints;
+import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
+import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency;
+import org.simantics.g2d.canvas.impl.HintReflection.HintListener;
+import org.simantics.scenegraph.g2d.events.MouseEvent;
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
+import org.simantics.utils.datastructures.hints.IHintObservable;
+import org.simantics.utils.datastructures.hints.IHintContext.Key;
+
+/**
+ * MouseUtil tracks position and button status of mice.
+ * It remembers where mouse was on control and on diagram when each of its button
+ * was pressed. 
+ * 
+ * MouseUtil generates synthetic mouse events {@link MouseClickEvent} and
+ * {@link MouseDragBegin} for convenience. 
+ * 
+ * It also tracks distance how far mouse has moved since each of its 
+ * button was pressed (used for click).
+ * <p>
+ * There is also debug cursor painter which can be enabled/disabled with 
+ * hint KEY_CURSOR_STATUS.
+ * 
+ * @TODO From to mouse monitor and mouse painter classes
+ */
+public class MouseUtil extends AbstractCanvasParticipant {     
+
+    protected @Dependency TransformUtil util;
+
+    /** Mice info */
+    protected  Map<Integer, MouseInfo> miceInfo = new HashMap<Integer, MouseInfo>();
+    protected  Map<Integer, MouseInfo> micePressedInfo = new HashMap<Integer, MouseInfo>();
+
+    public static class MouseClickProfile {
+        public static final MouseClickProfile DEFAULT = new MouseClickProfile(); 
+        /** Maximum time of holding a button down that counts as a click */ 
+        public long clickHoldTimeTolerance = 300L;
+        /** Maximum number of pixels mouse may move while pressed to count a click */ 
+        public double movementTolerance = 3.5;
+        /** Maximum time to wait for clicks to count as consecutive */
+        public long consecutiveToleranceTime = 380L;
+    }
+
+    protected MouseClickProfile profile = MouseClickProfile.DEFAULT;
+
+    @HintListener(Class=Hints.class, Field="KEY_CANVAS_TRANSFORM")
+    public void transformChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
+        if (oldValue != null && oldValue.equals(newValue)) return;
+        sendMicePos();
+    }
+
+    @EventHandler(priority = Integer.MAX_VALUE)
+    public boolean handleMouseEvent(MouseEvent e) 
+    {
+        assertDependencies();
+        if (e instanceof MouseEnterEvent)
+        {
+            Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
+            // Reset mouse
+            MouseInfo mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);
+            miceInfo.put(e.mouseId, mi);
+        } else 
+            if (e instanceof MouseExitEvent)
+            {
+                miceInfo.remove(e.mouseId);
+            } else
+                if (e instanceof MouseMovedEvent)
+                {
+                    Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
+                    double deltaDistance = 0;
+                    MouseInfo mi = miceInfo.get(e.mouseId);
+                    if (mi==null) {
+                        mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, 0/*e.buttons*/);
+                        miceInfo.put(e.mouseId, mi);
+                    } else {
+                        deltaDistance = e.controlPosition.distance(mi.controlPosition);
+                        mi.controlPosition = e.controlPosition;
+                        mi.canvasPosition = canvasPosition;
+                    }
+
+                    if (deltaDistance>0)
+                        mi.addDistanceForButtons(deltaDistance);
+
+                    // Send mouse drag events.
+                    for (ButtonInfo bi : mi.buttonPressInfo.values())
+                    {
+                        if (!bi.down) continue;
+                        if (bi.deltaMotion<=profile.movementTolerance) continue;
+                        if (bi.drag) continue;
+                        bi.drag = true;
+                        MouseDragBegin db = new MouseDragBegin(
+                                this, e.time, e.mouseId, e.buttons, e.stateMask, bi.button,
+                                bi.canvasPosition, bi.controlPosition,
+                                e.controlPosition, e.screenPosition
+                                );
+                        getContext().getEventQueue().queueFirst(db);
+                    }
+
+                } else
+                    if (e instanceof MouseButtonPressedEvent)
+                    {
+                        Point2D canvasPosition = util.controlToCanvas(e.controlPosition, null);
+                        MouseButtonPressedEvent me = (MouseButtonPressedEvent) e;
+                        MouseInfo mi = miceInfo.get(e.mouseId);
+                        if (mi==null) {
+                            mi = new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons);
+                            miceInfo.put(e.mouseId, mi);
+                            micePressedInfo.put(e.mouseId, mi);
+                        } else {
+                            mi.controlPosition = e.controlPosition;
+                            mi.canvasPosition = canvasPosition;
+                            micePressedInfo.put(e.mouseId, 
+                                    new MouseInfo(e.mouseId, e.controlPosition, canvasPosition, e.buttons));
+                        }                      
+                        mi.setButtonPressed(me.button, e.stateMask, e.controlPosition, canvasPosition, e.time);
+                    } else if (e instanceof MouseButtonReleasedEvent) {
+                        MouseButtonReleasedEvent me = (MouseButtonReleasedEvent) e;
+                        Point2D canvasPosition = util.controlToCanvas(me.controlPosition, null);
+                        MouseInfo mi = miceInfo.get(me.mouseId);
+                        if (mi==null) {
+                            mi = new MouseInfo(e.mouseId, me.controlPosition, canvasPosition, 0/*me.buttons*/);
+                            miceInfo.put(me.mouseId, mi);
+                        } else {
+                            mi.controlPosition = me.controlPosition;
+                            mi.canvasPosition = canvasPosition;
+                        }
+                        ButtonInfo bi = mi.releaseButton(me.button, me.time);
+                        if (bi==null) return false;
+
+                        if (me.holdTime > profile.clickHoldTimeTolerance) return false;
+                        if (bi.deltaMotion>profile.movementTolerance) return false;
+                        // This is a click
+
+                        long timeSinceLastClick = me.time - bi.lastClickEventTime;
+                        bi.lastClickEventTime = me.time;
+
+                        // reset click counter
+                        if (timeSinceLastClick>profile.consecutiveToleranceTime) 
+                            bi.clickCount = 1;
+                        else
+                            bi.clickCount++;
+
+                        MouseClickEvent ce = 
+                                new MouseClickEvent(getContext(), e.time, e.mouseId, e.buttons, e.stateMask, me.button, bi.clickCount, me.controlPosition, me.screenPosition);                         
+                        getContext().getEventQueue().queueFirst(ce);
+                    }
+        return false;
+    }
+
+    /**
+     * Get last known position of a mouse
+     * 
+     * @param mouseId mouse id
+     * @return mouse position in canvas coordinates or null if mouse exited
+     *         canvas
+     */
+    public MouseInfo getMouseInfo(int mouseId) {
+        return miceInfo.get(mouseId);
+    }
+
+    public MouseInfo getMousePressedInfo(int mouseId) {
+        return micePressedInfo.get(mouseId);
+    }
+
+    /**
+     * Get button info
+     * @param mouseId mouse id
+     * @param buttonId button id
+     * @return null if button has never been pressed, or button info
+     */
+    public ButtonInfo getButtonInfo(int mouseId, int buttonId)
+    {
+        MouseInfo mi = miceInfo.get(mouseId);
+        if (mi==null) return null;
+        return mi.getButtonPressInfo(buttonId);
+    }
+
+    /**
+     * Get a snapshot of statuses of all mice
+     * @return a snapshot
+     */
+    public Map<Integer, MouseInfo> getMiceInfo()
+    {
+        return new HashMap<Integer, MouseInfo>(miceInfo);
+    }
+
+    public int getMouseCount()
+    {
+        return miceInfo.size();
+    }
+
+    public MouseClickProfile getProfile() {
+        return profile;
+    }
+
+    public void setProfile(MouseClickProfile profile) {
+        assert(profile!=null);
+        this.profile = profile;
+    }
+
+    public static final class MouseInfo {
+        public int mouseId;
+        public Point2D controlPosition;
+        public Point2D canvasPosition;
+        public int buttons;
+        public MouseInfo(int mouseId, Point2D initialControlPos, Point2D initialCanvasPosition, int initialButtonMask) {
+            this.mouseId = mouseId;
+            this.controlPosition = initialControlPos;
+            this.canvasPosition = initialCanvasPosition;
+            int buttonId = 0;
+            while (initialButtonMask!=0) {
+                if ((initialButtonMask & 1)==1)
+                    setButtonPressed(buttonId, 0, initialControlPos, initialCanvasPosition, Long.MIN_VALUE);
+                initialButtonMask >>>= 1;
+                buttonId++;
+            }
+        }
+
+        public boolean isMouseButtonPressed(int buttonId)
+        {
+            ButtonInfo bi = buttonPressInfo.get(buttonId);
+            return bi!=null && bi.down;
+        }
+        private void _countButtonMask() {
+            int result = 0;
+            for (ButtonInfo pi : buttonPressInfo.values())
+            {
+                if (!pi.down) continue;
+                result |= 1 << (pi.button-1);
+            }
+            this.buttons = result;
+        }
+        public  Map<Integer, ButtonInfo> buttonPressInfo = new HashMap<Integer, ButtonInfo>();
+        public void setButtonPressed(int buttonId, int stateMask, Point2D controlPos, Point2D canvasPos, long eventTime) {
+            ButtonInfo bi = getOrCreateButtonInfo(buttonId); 
+            bi.canvasPosition = canvasPos;
+            bi.controlPosition = controlPos;
+            bi.systemTime = System.currentTimeMillis();
+            bi.eventTime = eventTime;
+            bi.down = true;
+            bi.deltaMotion = 0;
+            bi.drag = false;
+            bi.stateMask = stateMask;
+            _countButtonMask();
+        }
+        public ButtonInfo releaseButton(int buttonId, long eventTime) {
+            ButtonInfo bi = getButtonPressInfo(buttonId);
+            if (bi==null) return null;
+            bi.down = false;
+            bi.holdTime = eventTime - bi.eventTime;
+            _countButtonMask();
+            return bi;
+        }
+        ButtonInfo getOrCreateButtonInfo(int buttonId)
+        {
+            ButtonInfo bi = buttonPressInfo.get(buttonId);
+            if (bi==null) bi = new ButtonInfo(buttonId);
+            buttonPressInfo.put(buttonId, bi);
+            return bi;
+        }
+        public ButtonInfo getButtonPressInfo(int buttonId) {
+            return buttonPressInfo.get(buttonId);
+        }
+        public void addDistanceForButtons(double distance) {
+            for (ButtonInfo bi : buttonPressInfo.values())
+            {
+                if (!bi.down) continue;
+                bi.deltaMotion += distance;
+            }
+        }
+        public Collection<ButtonInfo> getButtonInfos() {
+            return buttonPressInfo.values();
+        }
+    }
+
+    /** Status of mouse's button press */
+    public static final class ButtonInfo {
+        /** Position on press */
+        public Point2D controlPosition;
+        /** Position on press */
+        public Point2D canvasPosition;
+        public final int button;
+        public int stateMask;
+        /** System time when pressed */
+        public long systemTime;
+        /** Event time when pressed */
+        public long eventTime;
+        /** Hold time when released */
+        public long holdTime;
+        /** Current up / down status */
+        public boolean down = false;
+        /** Total movement in pixels since press */ 
+        public double deltaMotion = 0.0;
+        /** Click Count */
+        public int clickCount = 0;
+        /** Time of last click */
+        public long lastClickEventTime = Long.MIN_VALUE;
+        /** Dragged (set true when possibility for click is excluded) */
+        public boolean drag = false;
+        public ButtonInfo(int button) {
+            this.button = button;
+        }
+    }
+
+    /**
+     * Sends mice positions as an event
+     */
+    private void sendMicePos()
+    {
+        long time = System.currentTimeMillis();
+        for (Entry<Integer, MouseInfo> e : miceInfo.entrySet()) {
+            MouseInfo mi = e.getValue();
+            MouseMovedEvent mme;
+            // FIXME: screenPosition as null (requires adding screenPos into MouseInfo)
+            mme = new MouseMovedEvent(getContext(), time, e.getKey(), mi.buttons, 0 /* FIXME ??? */, mi.controlPosition, null);
+            getContext().getEventQueue().queueEvent(mme);
+        }
+    }
+
+}