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