]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/diagram/participant/Selection.java
Sync git svn branch with SVN repository r33269.
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / diagram / participant / Selection.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.g2d.diagram.participant;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Collection;\r
16 import java.util.Collections;\r
17 import java.util.HashMap;\r
18 import java.util.HashSet;\r
19 import java.util.Map;\r
20 import java.util.Set;\r
21 import java.util.Map.Entry;\r
22 \r
23 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;\r
24 import org.simantics.g2d.diagram.DiagramHints;\r
25 import org.simantics.g2d.diagram.IDiagram;\r
26 import org.simantics.g2d.element.IElement;\r
27 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;\r
28 import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
29 import org.simantics.scenegraph.g2d.events.command.Commands;\r
30 import org.simantics.utils.DataContainer;\r
31 import org.simantics.utils.datastructures.collections.CollectionUtils;\r
32 import org.simantics.utils.datastructures.hints.IHintContext.Key;\r
33 import org.simantics.utils.datastructures.hints.IHintContext.MouseSpecificKeyOf;\r
34 import org.simantics.utils.threads.ThreadUtils;\r
35 \r
36 /**\r
37  * Selection is a canvas utility used for managing selected elements.\r
38  * <p>\r
39  * There are multiple selection context. They are differentiated with selectionId.\r
40  * Multiple selections are used in multi-user environments (such as multi touch screen).\r
41  * In normal diagram setup SELECTION0 is the default selection.\r
42  * \r
43  * @author Toni Kalajainen\r
44  */\r
45 public class Selection extends AbstractCanvasParticipant {\r
46 \r
47     /** Key for the most common Selection0 */\r
48     public static final SelectionHintKey SELECTION0 = new SelectionHintKey(0);\r
49 \r
50     private static final Set<IElement> NO_SELECTION = Collections\r
51     .unmodifiableSet(new HashSet<IElement>(0));\r
52 \r
53     public static SelectionHintKey getKeyForSelectionId(int selectionId) {\r
54         if (selectionId == 0)\r
55             return SELECTION0;\r
56         return new SelectionHintKey(selectionId);\r
57     }\r
58 \r
59     @EventHandler(priority = 0)\r
60     public boolean handleCommand(CommandEvent e)\r
61     {\r
62         if (e.command.equals( Commands.SELECT_ALL )) {\r
63             IDiagram d = getHint( DiagramHints.KEY_DIAGRAM );\r
64             if (d==null) return true;\r
65             addAll(0, d.getElements());\r
66             return true;\r
67         }\r
68         if (e.command.equals( Commands.INVERT_SELECTION )) {\r
69             IDiagram d = getHint( DiagramHints.KEY_DIAGRAM );\r
70             if (d==null) return true;\r
71             Set<IElement> current = getSelection(0);\r
72             Set<IElement> inverted = new HashSet<IElement>(d.getElements());\r
73             inverted.removeAll(current);\r
74             setSelection(0, inverted);\r
75             return true;\r
76         }\r
77         return false;\r
78     }\r
79 \r
80 \r
81     /**\r
82      * Get selection\r
83      * \r
84      * @param selectionId\r
85      *            selectionId\r
86      * @return returns a set of pickables\r
87      */\r
88     public Set<IElement> getSelection(int selectionId) {\r
89         Key key = getKeyForSelectionId(selectionId);\r
90         Set<IElement> selection = getHint(key);\r
91         if (selection == null)\r
92             return NO_SELECTION;\r
93         return new HashSet<IElement>(selection);\r
94     }\r
95 \r
96     /**\r
97      * Get all selections of all selection ids\r
98      * @return all selections\r
99      */\r
100     public Set<IElement> getAllSelections() {\r
101         Set<IElement> result = new HashSet<IElement>();\r
102         for (Entry<SelectionHintKey, Object> entry : getContext()\r
103                 .getHintStack().getHintsOfClass(SelectionHintKey.class)\r
104                 .entrySet()) {\r
105             @SuppressWarnings("unchecked")\r
106             Set<IElement> set = (Set<IElement>) entry.getValue();\r
107             if (set == null || set.isEmpty())\r
108                 continue;\r
109             result.addAll(set);\r
110         }\r
111         return result;\r
112 \r
113     }\r
114 \r
115     /**\r
116      * Get selections by selection id\r
117      * \r
118      * @return map of selection ids and selections\r
119      */\r
120     @SuppressWarnings("unchecked")\r
121     public Map<Integer, Set<IElement>> getSelections() {\r
122         Map<Integer, Set<IElement>> result = new HashMap<Integer, Set<IElement>>();\r
123         for (Entry<SelectionHintKey, Object> entry : getContext()\r
124                 .getHintStack().getHintsOfClass(SelectionHintKey.class)\r
125                 .entrySet()) {\r
126             Set<IElement> set = (Set<IElement>) entry.getValue();\r
127             if (set == null || set.isEmpty())\r
128                 continue;\r
129             result.put(entry.getKey().mouseId, set);\r
130         }\r
131         return result;\r
132     }\r
133 \r
134     public int[] getSelectionIds() {\r
135         Map<SelectionHintKey, Object> map = getContext().getHintStack()\r
136         .getHintsOfClass(SelectionHintKey.class);\r
137         int result[] = new int[map.size()];\r
138         int i = 0;\r
139         for (SelectionHintKey key : map.keySet())\r
140             result[i++] = key.mouseId;\r
141         return result;\r
142     }\r
143 \r
144     public boolean setSelection(final int selectionId,\r
145             final Collection<IElement> _selection) {\r
146         final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
147         ThreadUtils.syncExec(getThread(), new Runnable() {\r
148             @Override\r
149             public void run() {\r
150                 Collection<IElement> selection = _selection;\r
151                 Key key = getKeyForSelectionId(selectionId);\r
152                 if (selection == null || selection.isEmpty())\r
153                     selection = NO_SELECTION;\r
154                 Set<IElement> oldSelection = getHint(key);\r
155                 if (oldSelection == null)\r
156                     oldSelection = NO_SELECTION;\r
157                 if (oldSelection.equals(selection))\r
158                     return;\r
159                 if (selection == NO_SELECTION) {\r
160                     removeHint(key);\r
161                     result.set(true);\r
162                 }\r
163                 Set<IElement> newSelection = Collections\r
164                 .unmodifiableSet(new HashSet<IElement>(selection));\r
165                 setHint(key, newSelection);\r
166                 result.set(true);\r
167             }\r
168         });\r
169         return result.get();\r
170     }\r
171 \r
172     public boolean setSelection(int selectionId, IElement selection) {\r
173         ArrayList<IElement> list = new ArrayList<IElement>(1);\r
174         list.add(selection);\r
175         return setSelection(selectionId, list);\r
176     }\r
177 \r
178     /**\r
179      * Add item to selection\r
180      * \r
181      * @param pickable\r
182      */\r
183     public boolean add(final int selectionId, final IElement pickable) {\r
184         final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
185         ThreadUtils.syncExec(getThread(), new Runnable() {\r
186             @Override\r
187             public void run() {\r
188                 Key key = getKeyForSelectionId(selectionId);\r
189                 Set<IElement> oldSelection = getSelection(selectionId);\r
190                 if (oldSelection.contains(pickable))\r
191                     return;\r
192                 Set<IElement> newSelection = new HashSet<IElement>(\r
193                         oldSelection);\r
194                 newSelection.add(pickable);\r
195                 newSelection = Collections.unmodifiableSet(newSelection);\r
196                 setHint(key, newSelection);\r
197                 result.set(true);\r
198             }\r
199         });\r
200         return result.get();\r
201     }\r
202 \r
203     /**\r
204      * Add items to mouse0's selection\r
205      * \r
206      * @param pickables\r
207      * @return\r
208      */\r
209     public boolean addAll(final int selectionId,\r
210             final Collection<IElement> pickables) {\r
211         final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
212         ThreadUtils.syncExec(getThread(), new Runnable() {\r
213             @Override\r
214             public void run() {\r
215 \r
216                 Key key = getKeyForSelectionId(selectionId);\r
217                 Set<IElement> selection = getSelection(selectionId);\r
218                 if (selection.containsAll(pickables))\r
219                     return;\r
220                 Set<IElement> newSelection = new HashSet<IElement>(selection);\r
221                 newSelection.addAll(pickables);\r
222                 newSelection = Collections.unmodifiableSet(newSelection);\r
223                 setHint(key, newSelection);\r
224                 result.set(true);\r
225             }\r
226         });\r
227         return result.get();\r
228     }\r
229 \r
230     /**\r
231      * Remove an item from mouse0's selection\r
232      * \r
233      * @param pickable\r
234      * @return\r
235      */\r
236     public boolean remove(final int selectionId, final IElement pickable) {\r
237         final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
238         ThreadUtils.syncExec(getThread(), new Runnable() {\r
239             @Override\r
240             public void run() {\r
241                 Key key = getKeyForSelectionId(selectionId);\r
242                 Set<IElement> oldSelection = getSelection(selectionId);\r
243                 if (!oldSelection.contains(pickable))\r
244                     return;\r
245                 Set<IElement> newSelection = new HashSet<IElement>(\r
246                         oldSelection);\r
247                 newSelection.remove(pickable);\r
248                 newSelection = Collections.unmodifiableSet(newSelection);\r
249                 setHint(key, newSelection);\r
250                 result.set(true);\r
251             }\r
252         });\r
253         return result.get();\r
254     }\r
255 \r
256     /**\r
257      * Returns true if two collections have a common object\r
258      * \r
259      * @param x\r
260      * @param y\r
261      * @return\r
262      */\r
263     public static boolean containsAny(Collection<?> x, Collection<?> y) {\r
264         for (Object o : x)\r
265             if (y.contains(o))\r
266                 return true;\r
267         return false;\r
268     }\r
269 \r
270     /**\r
271      * Remove items from mouse0's selection\r
272      * \r
273      * @param pickables\r
274      * @return\r
275      */\r
276     public boolean removeAll(final int selectionId,\r
277             final Collection<IElement> pickables) {\r
278         final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
279         ThreadUtils.syncExec(getThread(), new Runnable() {\r
280             @Override\r
281             public void run() {\r
282                 Key key = getKeyForSelectionId(selectionId);\r
283                 Set<IElement> oldSelection = getSelection(selectionId);\r
284                 if (!containsAny(oldSelection, pickables))\r
285                     return;\r
286                 Set<IElement> newSelection = new HashSet<IElement>(\r
287                         oldSelection);\r
288                 newSelection.removeAll(pickables);\r
289                 newSelection = Collections.unmodifiableSet(newSelection);\r
290                 setHint(key, newSelection);\r
291                 result.set(true);\r
292             }\r
293         });\r
294         return result.get();\r
295     }\r
296 \r
297     /**\r
298      * Retain items in mouse0's selection\r
299      * \r
300      * @param pickable\r
301      * @return\r
302      */\r
303     public boolean retainAll(final int selectionId,\r
304             final Collection<IElement> pickable) {\r
305         final DataContainer<Boolean> result = new DataContainer<Boolean>(false);\r
306         ThreadUtils.syncExec(getThread(), new Runnable() {\r
307             @Override\r
308             public void run() {\r
309                 Key key = getKeyForSelectionId(selectionId);\r
310                 Set<IElement> oldSelection = getSelection(selectionId);\r
311                 Set<IElement> newSelection = new HashSet<IElement>(\r
312                         oldSelection);\r
313                 newSelection.retainAll(pickable);\r
314                 if (oldSelection.equals(newSelection))\r
315                     return;\r
316                 newSelection = Collections.unmodifiableSet(newSelection);\r
317                 setHint(key, newSelection);\r
318                 result.set(true);\r
319             }\r
320         });\r
321         return result.get();\r
322     }\r
323 \r
324     public boolean contains(int selectionId, IElement pickable) {\r
325         Set<IElement> oldSelection = getSelection(selectionId);\r
326         return oldSelection.contains(pickable);\r
327     }\r
328 \r
329     public synchronized void toggle(final int selectionId, final Set<IElement> toggleSet) {\r
330         ThreadUtils.syncExec(getThread(), new Runnable() {\r
331             @Override\r
332             public void run() {\r
333                 Key key = getKeyForSelectionId(selectionId);\r
334                 Set<IElement> oldSelection = getSelection(selectionId);\r
335                 Set<IElement> newSelection = new HashSet<IElement>(oldSelection);\r
336                 CollectionUtils.toggle(newSelection, toggleSet);\r
337                 newSelection = Collections.unmodifiableSet(newSelection);\r
338                 setHint(key, newSelection);\r
339             }\r
340         });\r
341     }\r
342 \r
343     /**\r
344      * Toggle an item in a selection.\r
345      */\r
346     public synchronized void toggle(int selectionId, IElement pickable) {\r
347         Key key = getKeyForSelectionId(selectionId);\r
348         Set<IElement> oldSelection = getSelection(selectionId);\r
349         Set<IElement> newSelection = new HashSet<IElement>(oldSelection);\r
350 \r
351         if (oldSelection.contains(pickable))\r
352             newSelection.remove(pickable);\r
353         else\r
354             newSelection.add(pickable);\r
355 \r
356         newSelection = Collections.unmodifiableSet(newSelection);\r
357         setHint(key, newSelection);\r
358     }\r
359 \r
360     /**\r
361      * Clear selection\r
362      */\r
363     public void clear(int selectionId) {\r
364         Key key = getKeyForSelectionId(selectionId);\r
365         removeHint(key);\r
366     }\r
367 \r
368     /** Class for mouse specific hint keys */\r
369     public static class SelectionHintKey extends MouseSpecificKeyOf {\r
370         public SelectionHintKey(int mouseId) {\r
371             super(mouseId, Set.class);\r
372         }\r
373     }\r
374 \r
375 \r
376 }\r