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
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.g2d.diagram.participant;
\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
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
37 * Selection is a canvas utility used for managing selected elements.
\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
43 * @author Toni Kalajainen
\r
45 public class Selection extends AbstractCanvasParticipant {
\r
47 /** Key for the most common Selection0 */
\r
48 public static final SelectionHintKey SELECTION0 = new SelectionHintKey(0);
\r
50 private static final Set<IElement> NO_SELECTION = Collections
\r
51 .unmodifiableSet(new HashSet<IElement>(0));
\r
53 public static SelectionHintKey getKeyForSelectionId(int selectionId) {
\r
54 if (selectionId == 0)
\r
56 return new SelectionHintKey(selectionId);
\r
59 @EventHandler(priority = 0)
\r
60 public boolean handleCommand(CommandEvent e)
\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
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
84 * @param selectionId
\r
86 * @return returns a set of pickables
\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
97 * Get all selections of all selection ids
\r
98 * @return all selections
\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
105 @SuppressWarnings("unchecked")
\r
106 Set<IElement> set = (Set<IElement>) entry.getValue();
\r
107 if (set == null || set.isEmpty())
\r
109 result.addAll(set);
\r
116 * Get selections by selection id
\r
118 * @return map of selection ids and selections
\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
126 Set<IElement> set = (Set<IElement>) entry.getValue();
\r
127 if (set == null || set.isEmpty())
\r
129 result.put(entry.getKey().mouseId, set);
\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
139 for (SelectionHintKey key : map.keySet())
\r
140 result[i++] = key.mouseId;
\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
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
159 if (selection == NO_SELECTION) {
\r
163 Set<IElement> newSelection = Collections
\r
164 .unmodifiableSet(new HashSet<IElement>(selection));
\r
165 setHint(key, newSelection);
\r
169 return result.get();
\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
179 * Add item to selection
\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
187 public void run() {
\r
188 Key key = getKeyForSelectionId(selectionId);
\r
189 Set<IElement> oldSelection = getSelection(selectionId);
\r
190 if (oldSelection.contains(pickable))
\r
192 Set<IElement> newSelection = new HashSet<IElement>(
\r
194 newSelection.add(pickable);
\r
195 newSelection = Collections.unmodifiableSet(newSelection);
\r
196 setHint(key, newSelection);
\r
200 return result.get();
\r
204 * Add items to mouse0's selection
\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
214 public void run() {
\r
216 Key key = getKeyForSelectionId(selectionId);
\r
217 Set<IElement> selection = getSelection(selectionId);
\r
218 if (selection.containsAll(pickables))
\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
227 return result.get();
\r
231 * Remove an item from mouse0's selection
\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
240 public void run() {
\r
241 Key key = getKeyForSelectionId(selectionId);
\r
242 Set<IElement> oldSelection = getSelection(selectionId);
\r
243 if (!oldSelection.contains(pickable))
\r
245 Set<IElement> newSelection = new HashSet<IElement>(
\r
247 newSelection.remove(pickable);
\r
248 newSelection = Collections.unmodifiableSet(newSelection);
\r
249 setHint(key, newSelection);
\r
253 return result.get();
\r
257 * Returns true if two collections have a common object
\r
263 public static boolean containsAny(Collection<?> x, Collection<?> y) {
\r
271 * Remove items from mouse0's selection
\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
281 public void run() {
\r
282 Key key = getKeyForSelectionId(selectionId);
\r
283 Set<IElement> oldSelection = getSelection(selectionId);
\r
284 if (!containsAny(oldSelection, pickables))
\r
286 Set<IElement> newSelection = new HashSet<IElement>(
\r
288 newSelection.removeAll(pickables);
\r
289 newSelection = Collections.unmodifiableSet(newSelection);
\r
290 setHint(key, newSelection);
\r
294 return result.get();
\r
298 * Retain items in mouse0's selection
\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
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
313 newSelection.retainAll(pickable);
\r
314 if (oldSelection.equals(newSelection))
\r
316 newSelection = Collections.unmodifiableSet(newSelection);
\r
317 setHint(key, newSelection);
\r
321 return result.get();
\r
324 public boolean contains(int selectionId, IElement pickable) {
\r
325 Set<IElement> oldSelection = getSelection(selectionId);
\r
326 return oldSelection.contains(pickable);
\r
329 public synchronized void toggle(final int selectionId, final Set<IElement> toggleSet) {
\r
330 ThreadUtils.syncExec(getThread(), new Runnable() {
\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
344 * Toggle an item in a selection.
\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
351 if (oldSelection.contains(pickable))
\r
352 newSelection.remove(pickable);
\r
354 newSelection.add(pickable);
\r
356 newSelection = Collections.unmodifiableSet(newSelection);
\r
357 setHint(key, newSelection);
\r
363 public void clear(int selectionId) {
\r
364 Key key = getKeyForSelectionId(selectionId);
\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