1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.diagram.participant;
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;
21 import java.util.Map.Entry;
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;
37 * Selection is a canvas utility used for managing selected elements.
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.
43 * @author Toni Kalajainen
45 public class Selection extends AbstractCanvasParticipant {
47 /** Key for the most common Selection0 */
48 public static final SelectionHintKey SELECTION0 = new SelectionHintKey(0);
50 private static final Set<IElement> NO_SELECTION = Collections
51 .unmodifiableSet(new HashSet<IElement>(0));
53 public static SelectionHintKey getKeyForSelectionId(int selectionId) {
56 return new SelectionHintKey(selectionId);
59 @EventHandler(priority = 0)
60 public boolean handleCommand(CommandEvent e)
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());
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);
86 * @return returns a set of pickables
88 public Set<IElement> getSelection(int selectionId) {
89 Key key = getKeyForSelectionId(selectionId);
90 Set<IElement> selection = getHint(key);
91 if (selection == null)
93 return new HashSet<IElement>(selection);
97 * Get all selections of all selection ids
98 * @return all selections
100 public Set<IElement> getAllSelections() {
101 Set<IElement> result = new HashSet<IElement>();
102 for (Entry<SelectionHintKey, Object> entry : getContext()
103 .getHintStack().getHintsOfClass(SelectionHintKey.class)
105 @SuppressWarnings("unchecked")
106 Set<IElement> set = (Set<IElement>) entry.getValue();
107 if (set == null || set.isEmpty())
116 * Get selections by selection id
118 * @return map of selection ids and selections
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)
126 Set<IElement> set = (Set<IElement>) entry.getValue();
127 if (set == null || set.isEmpty())
129 result.put(entry.getKey().mouseId, set);
134 public int[] getSelectionIds() {
135 Map<SelectionHintKey, Object> map = getContext().getHintStack()
136 .getHintsOfClass(SelectionHintKey.class);
137 int result[] = new int[map.size()];
139 for (SelectionHintKey key : map.keySet())
140 result[i++] = key.mouseId;
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() {
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))
159 if (selection == NO_SELECTION) {
163 Set<IElement> newSelection = Collections
164 .unmodifiableSet(new HashSet<IElement>(selection));
165 setHint(key, newSelection);
172 public boolean setSelection(int selectionId, IElement selection) {
173 ArrayList<IElement> list = new ArrayList<IElement>(1);
175 return setSelection(selectionId, list);
179 * Add item to selection
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() {
188 Key key = getKeyForSelectionId(selectionId);
189 Set<IElement> oldSelection = getSelection(selectionId);
190 if (oldSelection.contains(pickable))
192 Set<IElement> newSelection = new HashSet<IElement>(
194 newSelection.add(pickable);
195 newSelection = Collections.unmodifiableSet(newSelection);
196 setHint(key, newSelection);
204 * Add items to mouse0's selection
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() {
216 Key key = getKeyForSelectionId(selectionId);
217 Set<IElement> selection = getSelection(selectionId);
218 if (selection.containsAll(pickables))
220 Set<IElement> newSelection = new HashSet<IElement>(selection);
221 newSelection.addAll(pickables);
222 newSelection = Collections.unmodifiableSet(newSelection);
223 setHint(key, newSelection);
231 * Remove an item from mouse0's selection
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() {
241 Key key = getKeyForSelectionId(selectionId);
242 Set<IElement> oldSelection = getSelection(selectionId);
243 if (!oldSelection.contains(pickable))
245 Set<IElement> newSelection = new HashSet<IElement>(
247 newSelection.remove(pickable);
248 newSelection = Collections.unmodifiableSet(newSelection);
249 setHint(key, newSelection);
257 * Returns true if two collections have a common object
263 public static boolean containsAny(Collection<?> x, Collection<?> y) {
271 * Remove items from mouse0's selection
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() {
282 Key key = getKeyForSelectionId(selectionId);
283 Set<IElement> oldSelection = getSelection(selectionId);
284 if (!containsAny(oldSelection, pickables))
286 Set<IElement> newSelection = new HashSet<IElement>(
288 newSelection.removeAll(pickables);
289 newSelection = Collections.unmodifiableSet(newSelection);
290 setHint(key, newSelection);
298 * Retain items in mouse0's selection
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() {
309 Key key = getKeyForSelectionId(selectionId);
310 Set<IElement> oldSelection = getSelection(selectionId);
311 Set<IElement> newSelection = new HashSet<IElement>(
313 newSelection.retainAll(pickable);
314 if (oldSelection.equals(newSelection))
316 newSelection = Collections.unmodifiableSet(newSelection);
317 setHint(key, newSelection);
324 public boolean contains(int selectionId, IElement pickable) {
325 Set<IElement> oldSelection = getSelection(selectionId);
326 return oldSelection.contains(pickable);
329 public synchronized void toggle(final int selectionId, final Set<IElement> toggleSet) {
330 ThreadUtils.syncExec(getThread(), new Runnable() {
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);
344 * Toggle an item in a selection.
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);
351 if (oldSelection.contains(pickable))
352 newSelection.remove(pickable);
354 newSelection.add(pickable);
356 newSelection = Collections.unmodifiableSet(newSelection);
357 setHint(key, newSelection);
363 public void clear(int selectionId) {
364 Key key = getKeyForSelectionId(selectionId);
368 /** Class for mouse specific hint keys */
369 public static class SelectionHintKey extends MouseSpecificKeyOf {
370 public SelectionHintKey(int mouseId) {
371 super(mouseId, Set.class);