--- /dev/null
+/*******************************************************************************\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.element.handler.impl;\r
+\r
+import java.awt.geom.Point2D;\r
+import java.lang.reflect.Method;\r
+import java.util.Map;\r
+\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.diagram.participant.DiagramParticipant;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.Clickable;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.KeyOf;\r
+import org.simantics.utils.threads.Executable;\r
+import org.simantics.utils.threads.IThreadWorkQueue;\r
+import org.simantics.utils.threads.SyncListenerList;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+/**\r
+ * Base implementation for button handlers\r
+ * \r
+ * \r
+ * @author Toni Kalajainen\r
+ */\r
+public abstract class AbstractClickable extends AbstractGrabbable implements Clickable {\r
+ \r
+ private static final long serialVersionUID = -8329973386869163106L;\r
+ \r
+ public static Key HOVER_KEY = new KeyOf(Boolean.class, "HOVER");\r
+ public static Key PRESS_STATUS_KEY = new KeyOf(PressStatus.class, "PRESS_STATUS");\r
+ \r
+ public AbstractClickable(Double strayDistance) {\r
+ super(1000.0);\r
+ }\r
+ \r
+ public AbstractClickable() {\r
+ super();\r
+ } \r
+\r
+ /**\r
+ * Get element press status\r
+ * @param e\r
+ * @param ctx\r
+ * @return\r
+ */\r
+ public PressStatus getPressStatus(IElement e, ICanvasContext ctx)\r
+ {\r
+ Map<Integer, GrabInfo> gis = getGrabs(e, ctx);\r
+ if (gis==null || gis.size()==0) {\r
+ Boolean hover = e.getHint(HOVER_KEY);\r
+ if (hover != null && hover)\r
+ return PressStatus.HOVER;\r
+ return PressStatus.NORMAL;\r
+ }\r
+ \r
+ // is held down \r
+ boolean held = false;\r
+ boolean pressing = false;\r
+ \r
+ for (GrabInfo gi : gis.values()) {\r
+ held = true;\r
+ pressing |= onPickCheck(e, ctx, gi.pointerId, gi.dragPosElement);\r
+ if (pressing) break;\r
+ } \r
+ \r
+ if (pressing) return PressStatus.PRESSED;\r
+ if (held) return PressStatus.HELD;\r
+ Boolean hover = e.getHint(HOVER_KEY);\r
+ if (hover != null && hover)\r
+ return PressStatus.HOVER;\r
+\r
+ return PressStatus.NORMAL;\r
+ }\r
+ \r
+ \r
+ @Override\r
+ public boolean handleMouseEvent(IElement e, ICanvasContext ctx,\r
+ MouseEvent me) {\r
+ //System.out.println("AbstractClickable.hME element:" + e + " me:" + me);\r
+ boolean b = super.handleMouseEvent(e, ctx, me);\r
+ Boolean hovering;\r
+ // DND drag starts causes DragBegin + ButtonReleasedEvents, and hovering must be set false\r
+ if (!(me instanceof MouseExitEvent || me instanceof MouseDragBegin || me instanceof MouseButtonReleasedEvent))\r
+ hovering = Boolean.valueOf(onPickCheck(e, ctx, me.mouseId, ElementUtils.controlToElementCoordinate(e, ctx, me.controlPosition, null)));\r
+ else\r
+ hovering = false;\r
+ if (!hovering.equals(e.getHint(HOVER_KEY))) {\r
+ e.setHint(HOVER_KEY, hovering);\r
+ }\r
+ \r
+ // hackety hack\r
+ PressStatus newStatus = getPressStatus(e, ctx);\r
+ if (!newStatus.equals(e.getHint(PRESS_STATUS_KEY)))\r
+ e.setHint(PRESS_STATUS_KEY, newStatus) ;\r
+ return b;\r
+ }\r
+ \r
+ @Override\r
+ protected void onDrag(GrabInfo gi, ICanvasContext ctx) {\r
+ \r
+ }\r
+\r
+ @Override\r
+ protected void onGrab(GrabInfo gi, ICanvasContext ctx) {\r
+ }\r
+\r
+ @Override\r
+ protected void onGrabCancel(GrabInfo gi, ICanvasContext ctx) {\r
+ }\r
+\r
+ @Override\r
+ protected void onRelease(GrabInfo gi, ICanvasContext ctx) {\r
+ // pick is pick until last mouse releases\r
+ if (getGrabCount(gi.e, ctx)>0) return;\r
+ \r
+ boolean pick = onPickCheck(gi.e, ctx, gi.pointerId, gi.dragPosElement);\r
+ if (pick) {\r
+ onClicked(gi, ctx);\r
+ fireClicked(gi.e, ctx);\r
+ }\r
+ }\r
+ \r
+ protected boolean onGrabCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos)\r
+ {\r
+ Point2D elementPos = ElementUtils.controlToElementCoordinate(e, ctx, pickPos, null); \r
+ return onPickCheck(e, ctx, pointerId, elementPos);\r
+ }\r
+ \r
+ protected abstract void onClicked(GrabInfo gi, ICanvasContext ctx);\r
+ \r
+ /**\r
+ * Pick check in element coordinates\r
+ * @param e\r
+ * @param ctx\r
+ * @param pointerId\r
+ * @param pickPos\r
+ * @return\r
+ */\r
+ protected abstract boolean onPickCheck(IElement e, ICanvasContext ctx, int pointerId, Point2D pickPos);\r
+\r
+\r
+ private static final Key KEY_CLICK_LISTENERS = new KeyOf(SyncListenerList.class);\r
+ \r
+ @Override\r
+ public void addListener(final IElement e, final ICanvasContext ctx, final IThreadWorkQueue thread, final ClickListener listener) {\r
+ ThreadUtils.syncExec(ctx.getThreadAccess(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ SyncListenerList<ClickListener> list = dp.getElementHint(e, KEY_CLICK_LISTENERS);\r
+ if (list==null) {\r
+ list = new SyncListenerList<ClickListener>(ClickListener.class);\r
+ dp.setElementHint(e, KEY_CLICK_LISTENERS, list);\r
+ }\r
+ list.add(thread, listener);\r
+ }}); \r
+ }\r
+\r
+ @Override\r
+ public void removeListener(final IElement e, final ICanvasContext ctx, final IThreadWorkQueue thread, final ClickListener listener) {\r
+ ThreadUtils.syncExec(ctx.getThreadAccess(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ SyncListenerList<ClickListener> list = dp.getElementHint(e, KEY_CLICK_LISTENERS);\r
+ if (list==null) return;\r
+ list.remove(thread, listener);\r
+ if (list.isEmpty())\r
+ dp.removeElementHint(e, KEY_CLICK_LISTENERS);\r
+ }}); \r
+ } \r
+ \r
+ private final static Method onClick = SyncListenerList.getMethod(ClickListener.class, "onClick");\r
+ public void fireClicked(IElement e, ICanvasContext ctx)\r
+ {\r
+ DiagramParticipant dp = ctx.getSingleItem(DiagramParticipant.class);\r
+ SyncListenerList<ClickListener> list = dp.getElementHint(e, KEY_CLICK_LISTENERS);\r
+ if (list==null) return;\r
+ Executable exes[] = list.getExecutables(onClick, e, ctx);\r
+ ThreadUtils.multiSyncExec(exes);\r
+ }\r
+ \r
+}\r