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;
19 import java.util.List;
21 import java.util.Map.Entry;
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;
41 * Selection is a canvas utility used for managing selected elements.
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.
47 * @author Toni Kalajainen
49 public class Selection extends AbstractCanvasParticipant {
51 /** Key for the most common Selection0 */
52 public static final SelectionHintKey SELECTION0 = new SelectionHintKey(0);
54 private static final Set<IElement> NO_SELECTION = Collections
55 .unmodifiableSet(new HashSet<IElement>(0));
57 public static SelectionHintKey getKeyForSelectionId(int selectionId) {
60 return new SelectionHintKey(selectionId);
63 @EventHandler(priority = 0)
64 public boolean handleCommand(CommandEvent e)
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()));
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));
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);
95 return filteredElements;
106 * @return returns a set of pickables
108 public Set<IElement> getSelection(int selectionId) {
109 Key key = getKeyForSelectionId(selectionId);
110 Set<IElement> selection = getHint(key);
111 if (selection == null)
113 return new HashSet<IElement>(selection);
117 * Get all selections of all selection ids
118 * @return all selections
120 public Set<IElement> getAllSelections() {
121 Set<IElement> result = new HashSet<IElement>();
122 for (Entry<SelectionHintKey, Object> entry : getContext()
123 .getHintStack().getHintsOfClass(SelectionHintKey.class)
125 @SuppressWarnings("unchecked")
126 Set<IElement> set = (Set<IElement>) entry.getValue();
127 if (set == null || set.isEmpty())
136 * Get selections by selection id
138 * @return map of selection ids and selections
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)
146 Set<IElement> set = (Set<IElement>) entry.getValue();
147 if (set == null || set.isEmpty())
149 result.put(entry.getKey().mouseId, set);
154 public int[] getSelectionIds() {
155 Map<SelectionHintKey, Object> map = getContext().getHintStack()
156 .getHintsOfClass(SelectionHintKey.class);
157 int result[] = new int[map.size()];
159 for (SelectionHintKey key : map.keySet())
160 result[i++] = key.mouseId;
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() {
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))
179 if (selection == NO_SELECTION) {
183 Set<IElement> newSelection = Collections
184 .unmodifiableSet(new HashSet<IElement>(selection));
185 setHint(key, newSelection);
192 public boolean setSelection(int selectionId, IElement selection) {
193 ArrayList<IElement> list = new ArrayList<IElement>(1);
195 return setSelection(selectionId, list);
199 * Add item to selection
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() {
208 Key key = getKeyForSelectionId(selectionId);
209 Set<IElement> oldSelection = getSelection(selectionId);
210 if (oldSelection.contains(pickable))
212 Set<IElement> newSelection = new HashSet<IElement>(
214 newSelection.add(pickable);
215 newSelection = Collections.unmodifiableSet(newSelection);
216 setHint(key, newSelection);
224 * Add items to mouse0's selection
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() {
236 Key key = getKeyForSelectionId(selectionId);
237 Set<IElement> selection = getSelection(selectionId);
238 if (selection.containsAll(pickables))
240 Set<IElement> newSelection = new HashSet<IElement>(selection);
241 newSelection.addAll(pickables);
242 newSelection = Collections.unmodifiableSet(newSelection);
243 setHint(key, newSelection);
251 * Remove an item from mouse0's selection
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() {
261 Key key = getKeyForSelectionId(selectionId);
262 Set<IElement> oldSelection = getSelection(selectionId);
263 if (!oldSelection.contains(pickable))
265 Set<IElement> newSelection = new HashSet<IElement>(
267 newSelection.remove(pickable);
268 newSelection = Collections.unmodifiableSet(newSelection);
269 setHint(key, newSelection);
277 * Returns true if two collections have a common object
283 public static boolean containsAny(Collection<?> x, Collection<?> y) {
291 * Remove items from mouse0's selection
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() {
302 Key key = getKeyForSelectionId(selectionId);
303 Set<IElement> oldSelection = getSelection(selectionId);
304 if (!containsAny(oldSelection, pickables))
306 Set<IElement> newSelection = new HashSet<IElement>(
308 newSelection.removeAll(pickables);
309 newSelection = Collections.unmodifiableSet(newSelection);
310 setHint(key, newSelection);
318 * Retain items in mouse0's selection
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() {
329 Key key = getKeyForSelectionId(selectionId);
330 Set<IElement> oldSelection = getSelection(selectionId);
331 Set<IElement> newSelection = new HashSet<IElement>(
333 newSelection.retainAll(pickable);
334 if (oldSelection.equals(newSelection))
336 newSelection = Collections.unmodifiableSet(newSelection);
337 setHint(key, newSelection);
344 public boolean contains(int selectionId, IElement pickable) {
345 Set<IElement> oldSelection = getSelection(selectionId);
346 return oldSelection.contains(pickable);
349 public synchronized void toggle(final int selectionId, final Set<IElement> toggleSet) {
350 ThreadUtils.syncExec(getThread(), new Runnable() {
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);
364 * Toggle an item in a selection.
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);
371 if (oldSelection.contains(pickable))
372 newSelection.remove(pickable);
374 newSelection.add(pickable);
376 newSelection = Collections.unmodifiableSet(newSelection);
377 setHint(key, newSelection);
383 public void clear(int selectionId) {
384 Key key = getKeyForSelectionId(selectionId);
388 /** Class for mouse specific hint keys */
389 public static class SelectionHintKey extends MouseSpecificKeyOf {
390 public SelectionHintKey(int mouseId) {
391 super(mouseId, Set.class);