/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.g2d.diagram.participant; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Map.Entry; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.diagram.DiagramHints; import org.simantics.g2d.diagram.IDiagram; import org.simantics.g2d.element.IElement; import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler; import org.simantics.scenegraph.g2d.events.command.CommandEvent; import org.simantics.scenegraph.g2d.events.command.Commands; import org.simantics.utils.DataContainer; import org.simantics.utils.datastructures.collections.CollectionUtils; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.datastructures.hints.IHintContext.MouseSpecificKeyOf; import org.simantics.utils.threads.ThreadUtils; /** * Selection is a canvas utility used for managing selected elements. *

* There are multiple selection context. They are differentiated with selectionId. * Multiple selections are used in multi-user environments (such as multi touch screen). * In normal diagram setup SELECTION0 is the default selection. * * @author Toni Kalajainen */ public class Selection extends AbstractCanvasParticipant { /** Key for the most common Selection0 */ public static final SelectionHintKey SELECTION0 = new SelectionHintKey(0); private static final Set NO_SELECTION = Collections .unmodifiableSet(new HashSet(0)); public static SelectionHintKey getKeyForSelectionId(int selectionId) { if (selectionId == 0) return SELECTION0; return new SelectionHintKey(selectionId); } @EventHandler(priority = 0) public boolean handleCommand(CommandEvent e) { if (e.command.equals( Commands.SELECT_ALL )) { IDiagram d = getHint( DiagramHints.KEY_DIAGRAM ); if (d==null) return true; addAll(0, d.getElements()); return true; } if (e.command.equals( Commands.INVERT_SELECTION )) { IDiagram d = getHint( DiagramHints.KEY_DIAGRAM ); if (d==null) return true; Set current = getSelection(0); Set inverted = new HashSet(d.getElements()); inverted.removeAll(current); setSelection(0, inverted); return true; } return false; } /** * Get selection * * @param selectionId * selectionId * @return returns a set of pickables */ public Set getSelection(int selectionId) { Key key = getKeyForSelectionId(selectionId); Set selection = getHint(key); if (selection == null) return NO_SELECTION; return new HashSet(selection); } /** * Get all selections of all selection ids * @return all selections */ public Set getAllSelections() { Set result = new HashSet(); for (Entry entry : getContext() .getHintStack().getHintsOfClass(SelectionHintKey.class) .entrySet()) { @SuppressWarnings("unchecked") Set set = (Set) entry.getValue(); if (set == null || set.isEmpty()) continue; result.addAll(set); } return result; } /** * Get selections by selection id * * @return map of selection ids and selections */ @SuppressWarnings("unchecked") public Map> getSelections() { Map> result = new HashMap>(); for (Entry entry : getContext() .getHintStack().getHintsOfClass(SelectionHintKey.class) .entrySet()) { Set set = (Set) entry.getValue(); if (set == null || set.isEmpty()) continue; result.put(entry.getKey().mouseId, set); } return result; } public int[] getSelectionIds() { Map map = getContext().getHintStack() .getHintsOfClass(SelectionHintKey.class); int result[] = new int[map.size()]; int i = 0; for (SelectionHintKey key : map.keySet()) result[i++] = key.mouseId; return result; } public boolean setSelection(final int selectionId, final Collection _selection) { final DataContainer result = new DataContainer(false); ThreadUtils.syncExec(getThread(), new Runnable() { @Override public void run() { Collection selection = _selection; Key key = getKeyForSelectionId(selectionId); if (selection == null || selection.isEmpty()) selection = NO_SELECTION; Set oldSelection = getHint(key); if (oldSelection == null) oldSelection = NO_SELECTION; if (oldSelection.equals(selection)) return; if (selection == NO_SELECTION) { removeHint(key); result.set(true); } Set newSelection = Collections .unmodifiableSet(new HashSet(selection)); setHint(key, newSelection); result.set(true); } }); return result.get(); } public boolean setSelection(int selectionId, IElement selection) { ArrayList list = new ArrayList(1); list.add(selection); return setSelection(selectionId, list); } /** * Add item to selection * * @param pickable */ public boolean add(final int selectionId, final IElement pickable) { final DataContainer result = new DataContainer(false); ThreadUtils.syncExec(getThread(), new Runnable() { @Override public void run() { Key key = getKeyForSelectionId(selectionId); Set oldSelection = getSelection(selectionId); if (oldSelection.contains(pickable)) return; Set newSelection = new HashSet( oldSelection); newSelection.add(pickable); newSelection = Collections.unmodifiableSet(newSelection); setHint(key, newSelection); result.set(true); } }); return result.get(); } /** * Add items to mouse0's selection * * @param pickables * @return */ public boolean addAll(final int selectionId, final Collection pickables) { final DataContainer result = new DataContainer(false); ThreadUtils.syncExec(getThread(), new Runnable() { @Override public void run() { Key key = getKeyForSelectionId(selectionId); Set selection = getSelection(selectionId); if (selection.containsAll(pickables)) return; Set newSelection = new HashSet(selection); newSelection.addAll(pickables); newSelection = Collections.unmodifiableSet(newSelection); setHint(key, newSelection); result.set(true); } }); return result.get(); } /** * Remove an item from mouse0's selection * * @param pickable * @return */ public boolean remove(final int selectionId, final IElement pickable) { final DataContainer result = new DataContainer(false); ThreadUtils.syncExec(getThread(), new Runnable() { @Override public void run() { Key key = getKeyForSelectionId(selectionId); Set oldSelection = getSelection(selectionId); if (!oldSelection.contains(pickable)) return; Set newSelection = new HashSet( oldSelection); newSelection.remove(pickable); newSelection = Collections.unmodifiableSet(newSelection); setHint(key, newSelection); result.set(true); } }); return result.get(); } /** * Returns true if two collections have a common object * * @param x * @param y * @return */ public static boolean containsAny(Collection x, Collection y) { for (Object o : x) if (y.contains(o)) return true; return false; } /** * Remove items from mouse0's selection * * @param pickables * @return */ public boolean removeAll(final int selectionId, final Collection pickables) { final DataContainer result = new DataContainer(false); ThreadUtils.syncExec(getThread(), new Runnable() { @Override public void run() { Key key = getKeyForSelectionId(selectionId); Set oldSelection = getSelection(selectionId); if (!containsAny(oldSelection, pickables)) return; Set newSelection = new HashSet( oldSelection); newSelection.removeAll(pickables); newSelection = Collections.unmodifiableSet(newSelection); setHint(key, newSelection); result.set(true); } }); return result.get(); } /** * Retain items in mouse0's selection * * @param pickable * @return */ public boolean retainAll(final int selectionId, final Collection pickable) { final DataContainer result = new DataContainer(false); ThreadUtils.syncExec(getThread(), new Runnable() { @Override public void run() { Key key = getKeyForSelectionId(selectionId); Set oldSelection = getSelection(selectionId); Set newSelection = new HashSet( oldSelection); newSelection.retainAll(pickable); if (oldSelection.equals(newSelection)) return; newSelection = Collections.unmodifiableSet(newSelection); setHint(key, newSelection); result.set(true); } }); return result.get(); } public boolean contains(int selectionId, IElement pickable) { Set oldSelection = getSelection(selectionId); return oldSelection.contains(pickable); } public synchronized void toggle(final int selectionId, final Set toggleSet) { ThreadUtils.syncExec(getThread(), new Runnable() { @Override public void run() { Key key = getKeyForSelectionId(selectionId); Set oldSelection = getSelection(selectionId); Set newSelection = new HashSet(oldSelection); CollectionUtils.toggle(newSelection, toggleSet); newSelection = Collections.unmodifiableSet(newSelection); setHint(key, newSelection); } }); } /** * Toggle an item in a selection. */ public synchronized void toggle(int selectionId, IElement pickable) { Key key = getKeyForSelectionId(selectionId); Set oldSelection = getSelection(selectionId); Set newSelection = new HashSet(oldSelection); if (oldSelection.contains(pickable)) newSelection.remove(pickable); else newSelection.add(pickable); newSelection = Collections.unmodifiableSet(newSelection); setHint(key, newSelection); } /** * Clear selection */ public void clear(int selectionId) { Key key = getKeyForSelectionId(selectionId); removeHint(key); } /** Class for mouse specific hint keys */ public static class SelectionHintKey extends MouseSpecificKeyOf { public SelectionHintKey(int mouseId) { super(mouseId, Set.class); } } }