--- /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.diagram.participant;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashMap;\r
+import java.util.HashSet;\r
+import java.util.Map;\r
+import java.util.Set;\r
+import java.util.Map.Entry;\r
+\r
+import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
+import org.simantics.scenegraph.g2d.events.command.Commands;\r
+import org.simantics.utils.DataContainer;\r
+import org.simantics.utils.datastructures.collections.CollectionUtils;\r
+import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
+import org.simantics.utils.datastructures.hints.IHintContext.MouseSpecificKeyOf;\r
+import org.simantics.utils.threads.ThreadUtils;\r
+\r
+/**\r
+ * Selection is a canvas utility used for managing selected elements.\r
+ * <p>\r
+ * There are multiple selection context. They are differentiated with selectionId.\r
+ * Multiple selections are used in multi-user environments (such as multi touch screen).\r
+ * In normal diagram setup SELECTION0 is the default selection.\r
+ * \r
+ * @author Toni Kalajainen\r
+ */\r
+public class Selection extends AbstractCanvasParticipant {\r
+\r
+ /** Key for the most common Selection0 */\r
+ public static final SelectionHintKey SELECTION0 = new SelectionHintKey(0);\r
+\r
+ private static final Set<IElement> NO_SELECTION = Collections\r
+ .unmodifiableSet(new HashSet<IElement>(0));\r
+\r
+ public static SelectionHintKey getKeyForSelectionId(int selectionId) {\r
+ if (selectionId == 0)\r
+ return SELECTION0;\r
+ return new SelectionHintKey(selectionId);\r
+ }\r
+\r
+ @EventHandler(priority = 0)\r
+ public boolean handleCommand(CommandEvent e)\r
+ {\r
+ if (e.command.equals( Commands.SELECT_ALL )) {\r
+ IDiagram d = getHint( DiagramHints.KEY_DIAGRAM );\r
+ if (d==null) return true;\r
+ addAll(0, d.getElements());\r
+ return true;\r
+ }\r
+ if (e.command.equals( Commands.INVERT_SELECTION )) {\r
+ IDiagram d = getHint( DiagramHints.KEY_DIAGRAM );\r
+ if (d==null) return true;\r
+ Set<IElement> current = getSelection(0);\r
+ Set<IElement> inverted = new HashSet<IElement>(d.getElements());\r
+ inverted.removeAll(current);\r
+ setSelection(0, inverted);\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+\r
+ /**\r
+ * Get selection\r
+ * \r
+ * @param selectionId\r
+ * selectionId\r
+ * @return returns a set of pickables\r
+ */\r
+ public Set<IElement> getSelection(int selectionId) {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> selection = getHint(key);\r
+ if (selection == null)\r
+ return NO_SELECTION;\r
+ return new HashSet<IElement>(selection);\r
+ }\r
+\r
+ /**\r
+ * Get all selections of all selection ids\r
+ * @return all selections\r
+ */\r
+ public Set<IElement> getAllSelections() {\r
+ Set<IElement> result = new HashSet<IElement>();\r
+ for (Entry<SelectionHintKey, Object> entry : getContext()\r
+ .getHintStack().getHintsOfClass(SelectionHintKey.class)\r
+ .entrySet()) {\r
+ @SuppressWarnings("unchecked")\r
+ Set<IElement> set = (Set<IElement>) entry.getValue();\r
+ if (set == null || set.isEmpty())\r
+ continue;\r
+ result.addAll(set);\r
+ }\r
+ return result;\r
+\r
+ }\r
+\r
+ /**\r
+ * Get selections by selection id\r
+ * \r
+ * @return map of selection ids and selections\r
+ */\r
+ @SuppressWarnings("unchecked")\r
+ public Map<Integer, Set<IElement>> getSelections() {\r
+ Map<Integer, Set<IElement>> result = new HashMap<Integer, Set<IElement>>();\r
+ for (Entry<SelectionHintKey, Object> entry : getContext()\r
+ .getHintStack().getHintsOfClass(SelectionHintKey.class)\r
+ .entrySet()) {\r
+ Set<IElement> set = (Set<IElement>) entry.getValue();\r
+ if (set == null || set.isEmpty())\r
+ continue;\r
+ result.put(entry.getKey().mouseId, set);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ public int[] getSelectionIds() {\r
+ Map<SelectionHintKey, Object> map = getContext().getHintStack()\r
+ .getHintsOfClass(SelectionHintKey.class);\r
+ int result[] = new int[map.size()];\r
+ int i = 0;\r
+ for (SelectionHintKey key : map.keySet())\r
+ result[i++] = key.mouseId;\r
+ return result;\r
+ }\r
+\r
+ public boolean setSelection(final int selectionId,\r
+ final Collection<IElement> _selection) {\r
+ final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
+ ThreadUtils.syncExec(getThread(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Collection<IElement> selection = _selection;\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ if (selection == null || selection.isEmpty())\r
+ selection = NO_SELECTION;\r
+ Set<IElement> oldSelection = getHint(key);\r
+ if (oldSelection == null)\r
+ oldSelection = NO_SELECTION;\r
+ if (oldSelection.equals(selection))\r
+ return;\r
+ if (selection == NO_SELECTION) {\r
+ removeHint(key);\r
+ result.set(true);\r
+ }\r
+ Set<IElement> newSelection = Collections\r
+ .unmodifiableSet(new HashSet<IElement>(selection));\r
+ setHint(key, newSelection);\r
+ result.set(true);\r
+ }\r
+ });\r
+ return result.get();\r
+ }\r
+\r
+ public boolean setSelection(int selectionId, IElement selection) {\r
+ ArrayList<IElement> list = new ArrayList<IElement>(1);\r
+ list.add(selection);\r
+ return setSelection(selectionId, list);\r
+ }\r
+\r
+ /**\r
+ * Add item to selection\r
+ * \r
+ * @param pickable\r
+ */\r
+ public boolean add(final int selectionId, final IElement pickable) {\r
+ final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
+ ThreadUtils.syncExec(getThread(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> oldSelection = getSelection(selectionId);\r
+ if (oldSelection.contains(pickable))\r
+ return;\r
+ Set<IElement> newSelection = new HashSet<IElement>(\r
+ oldSelection);\r
+ newSelection.add(pickable);\r
+ newSelection = Collections.unmodifiableSet(newSelection);\r
+ setHint(key, newSelection);\r
+ result.set(true);\r
+ }\r
+ });\r
+ return result.get();\r
+ }\r
+\r
+ /**\r
+ * Add items to mouse0's selection\r
+ * \r
+ * @param pickables\r
+ * @return\r
+ */\r
+ public boolean addAll(final int selectionId,\r
+ final Collection<IElement> pickables) {\r
+ final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
+ ThreadUtils.syncExec(getThread(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> selection = getSelection(selectionId);\r
+ if (selection.containsAll(pickables))\r
+ return;\r
+ Set<IElement> newSelection = new HashSet<IElement>(selection);\r
+ newSelection.addAll(pickables);\r
+ newSelection = Collections.unmodifiableSet(newSelection);\r
+ setHint(key, newSelection);\r
+ result.set(true);\r
+ }\r
+ });\r
+ return result.get();\r
+ }\r
+\r
+ /**\r
+ * Remove an item from mouse0's selection\r
+ * \r
+ * @param pickable\r
+ * @return\r
+ */\r
+ public boolean remove(final int selectionId, final IElement pickable) {\r
+ final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
+ ThreadUtils.syncExec(getThread(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> oldSelection = getSelection(selectionId);\r
+ if (!oldSelection.contains(pickable))\r
+ return;\r
+ Set<IElement> newSelection = new HashSet<IElement>(\r
+ oldSelection);\r
+ newSelection.remove(pickable);\r
+ newSelection = Collections.unmodifiableSet(newSelection);\r
+ setHint(key, newSelection);\r
+ result.set(true);\r
+ }\r
+ });\r
+ return result.get();\r
+ }\r
+\r
+ /**\r
+ * Returns true if two collections have a common object\r
+ * \r
+ * @param x\r
+ * @param y\r
+ * @return\r
+ */\r
+ public static boolean containsAny(Collection<?> x, Collection<?> y) {\r
+ for (Object o : x)\r
+ if (y.contains(o))\r
+ return true;\r
+ return false;\r
+ }\r
+\r
+ /**\r
+ * Remove items from mouse0's selection\r
+ * \r
+ * @param pickables\r
+ * @return\r
+ */\r
+ public boolean removeAll(final int selectionId,\r
+ final Collection<IElement> pickables) {\r
+ final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
+ ThreadUtils.syncExec(getThread(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> oldSelection = getSelection(selectionId);\r
+ if (!containsAny(oldSelection, pickables))\r
+ return;\r
+ Set<IElement> newSelection = new HashSet<IElement>(\r
+ oldSelection);\r
+ newSelection.removeAll(pickables);\r
+ newSelection = Collections.unmodifiableSet(newSelection);\r
+ setHint(key, newSelection);\r
+ result.set(true);\r
+ }\r
+ });\r
+ return result.get();\r
+ }\r
+\r
+ /**\r
+ * Retain items in mouse0's selection\r
+ * \r
+ * @param pickable\r
+ * @return\r
+ */\r
+ public boolean retainAll(final int selectionId,\r
+ final Collection<IElement> pickable) {\r
+ final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
+ ThreadUtils.syncExec(getThread(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> oldSelection = getSelection(selectionId);\r
+ Set<IElement> newSelection = new HashSet<IElement>(\r
+ oldSelection);\r
+ newSelection.retainAll(pickable);\r
+ if (oldSelection.equals(newSelection))\r
+ return;\r
+ newSelection = Collections.unmodifiableSet(newSelection);\r
+ setHint(key, newSelection);\r
+ result.set(true);\r
+ }\r
+ });\r
+ return result.get();\r
+ }\r
+\r
+ public boolean contains(int selectionId, IElement pickable) {\r
+ Set<IElement> oldSelection = getSelection(selectionId);\r
+ return oldSelection.contains(pickable);\r
+ }\r
+\r
+ public synchronized void toggle(final int selectionId, final Set<IElement> toggleSet) {\r
+ ThreadUtils.syncExec(getThread(), new Runnable() {\r
+ @Override\r
+ public void run() {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> oldSelection = getSelection(selectionId);\r
+ Set<IElement> newSelection = new HashSet<IElement>(oldSelection);\r
+ CollectionUtils.toggle(newSelection, toggleSet);\r
+ newSelection = Collections.unmodifiableSet(newSelection);\r
+ setHint(key, newSelection);\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * Toggle an item in a selection.\r
+ */\r
+ public synchronized void toggle(int selectionId, IElement pickable) {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ Set<IElement> oldSelection = getSelection(selectionId);\r
+ Set<IElement> newSelection = new HashSet<IElement>(oldSelection);\r
+\r
+ if (oldSelection.contains(pickable))\r
+ newSelection.remove(pickable);\r
+ else\r
+ newSelection.add(pickable);\r
+\r
+ newSelection = Collections.unmodifiableSet(newSelection);\r
+ setHint(key, newSelection);\r
+ }\r
+\r
+ /**\r
+ * Clear selection\r
+ */\r
+ public void clear(int selectionId) {\r
+ Key key = getKeyForSelectionId(selectionId);\r
+ removeHint(key);\r
+ }\r
+\r
+ /** Class for mouse specific hint keys */\r
+ public static class SelectionHintKey extends MouseSpecificKeyOf {\r
+ public SelectionHintKey(int mouseId) {\r
+ super(mouseId, Set.class);\r
+ }\r
+ }\r
+\r
+\r
+}\r