/******************************************************************************* * 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.diagram.ui; import java.util.ArrayList; import java.util.Collections; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import org.eclipse.jface.viewers.IPostSelectionProvider; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.ui.IWorkbenchPartSite; import org.simantics.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.diagram.elements.AdaptableImmutableProxyElement; import org.simantics.g2d.canvas.ICanvasContext; import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant; import org.simantics.g2d.canvas.impl.DependencyReflection.Dependency; import org.simantics.g2d.canvas.impl.HintReflection.HintListener; import org.simantics.g2d.diagram.participant.ElementJSON; import org.simantics.g2d.diagram.participant.Selection; import org.simantics.g2d.element.ElementHints; import org.simantics.g2d.element.IElement; import org.simantics.ui.selection.WorkbenchSelectionUtils; import org.simantics.utils.datastructures.hints.IHintObservable; import org.simantics.utils.datastructures.hints.IHintContext.Key; import org.simantics.utils.threads.IThreadWorkQueue; import org.simantics.utils.threads.ThreadUtils; /** * A canvas participant that listens to the #0 mouse selection and provides it * forward through the {@link IPostSelectionProvider} interface. * * @author Tuukka Lehtonen */ public class WorkbenchSelectionProvider extends AbstractCanvasParticipant implements IPostSelectionProvider, ElementJSON { private static final long POST_SELECTION_DELAY = 300; @Dependency protected Selection selection; protected IThreadWorkQueue swt; protected IWorkbenchPartSite site; protected ISelection currentSelection = StructuredSelection.EMPTY; protected CopyOnWriteArrayList listeners = new CopyOnWriteArrayList(); protected CopyOnWriteArrayList postListeners = new CopyOnWriteArrayList(); public WorkbenchSelectionProvider(IThreadWorkQueue swt) { this(swt, null); } public WorkbenchSelectionProvider(IThreadWorkQueue swt, IWorkbenchPartSite site) { this.swt = swt; this.site = site; if (site != null) site.setSelectionProvider(this); } @Override public void addedToContext(ICanvasContext ctx) { super.addedToContext(ctx); if (site != null && site.getSelectionProvider() != this) { swt.asyncExec(new Runnable() { @Override public void run() { if (site.getSelectionProvider() != WorkbenchSelectionProvider.this) site.setSelectionProvider(WorkbenchSelectionProvider.this); } }); } } @Override public void removedFromContext(ICanvasContext ctx) { if (site != null && site.getSelectionProvider() == this) { swt.asyncExec(new Runnable() { @Override public void run() { if (site.getSelectionProvider() == WorkbenchSelectionProvider.this) site.setSelectionProvider(null); } }); } super.removedFromContext(ctx); } @HintListener(Class = Selection.class, Field = "SELECTION0") public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { Iterable selection = (Iterable) newValue; final ISelection s = constructAdaptableSelection(selection); swt.asyncExec(new Runnable() { @Override public void run() { currentSelection = s; fireSelectionChanged(s); schedulePostSelectionChanged(s); } }); } @HintListener(Class = Selection.class, Field = "SELECTION0") public void hintRemoved(IHintObservable sender, Key key, Object oldValue) { //System.out.println("selection removed: " + oldValue); final ISelection s = constructAdaptableSelection(Collections.emptyList()); swt.asyncExec(new Runnable() { @Override public void run() { currentSelection = s; fireSelectionChanged(s); schedulePostSelectionChanged(s); } }); } // Post selection changed scheduling helper. private int modCount = 0; protected void schedulePostSelectionChanged(final ISelection s) { final int count = ++modCount; ThreadUtils.getNonBlockingWorkExecutor().schedule(new Runnable() { @Override public void run() { int newCount = modCount; if (count != newCount) return; if (isRemoved()) return; swt.asyncExec(new Runnable() { @Override public void run() { if (isRemoved()) return; firePostSelectionChanged(s); } }); } }, POST_SELECTION_DELAY, TimeUnit.MILLISECONDS); } protected ISelection constructAdaptableSelection(Iterable selection) { ArrayList objects = new ArrayList(); for (Object o : selection) { if (o instanceof IElement) { IElement e = (IElement) o; Object object = e.getHint(ElementHints.KEY_OBJECT); if (object != null) { objects.add(new AdaptableImmutableProxyElement(e)); } else { System.out.println(" discarding element from selection, null object for " + e); } } else { System.out.println(" unrecognized selection: " + o.getClass() + ": " + o); } } return new StructuredSelection(objects); } void fireSelectionChanged(ISelection selection) { SelectionChangedEvent e = new SelectionChangedEvent(this, selection); for (ISelectionChangedListener l : listeners) l.selectionChanged(e); } void firePostSelectionChanged(ISelection selection) { SelectionChangedEvent e = new SelectionChangedEvent(this, selection); for (ISelectionChangedListener l : postListeners) l.selectionChanged(e); } @Override public void addPostSelectionChangedListener(ISelectionChangedListener listener) { postListeners.add(listener); } @Override public void removePostSelectionChangedListener(ISelectionChangedListener listener) { postListeners.remove(listener); } @Override public void addSelectionChangedListener(ISelectionChangedListener listener) { listeners.add(listener); } @Override public void removeSelectionChangedListener(ISelectionChangedListener listener) { listeners.remove(listener); } @Override public ISelection getSelection() { return currentSelection; } @Override public void setSelection(ISelection selection) { System.out.println("WorkbenchSelectionProvider: TODO: set selection: " + selection); } @Override public Optional getJSON(IElement element) { ISelection sel = constructAdaptableSelection(Collections.singleton(element)); try { return Optional.ofNullable( WorkbenchSelectionUtils.getPossibleJSON(sel) ); } catch (DatabaseException e) { Logger.defaultLogError(e); return Optional.empty(); } } }