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