blob: c9b13587af33a4a9e522f2e7aa844ec0a094e7b9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Oliver Puetter - Bug 423040
* Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654
******************************************************************************/
package org.eclipse.e4.ui.internal.workbench;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.contexts.RunAndTrack;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.internal.contexts.EclipseContext;
import org.eclipse.e4.core.services.events.IEventBroker;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.services.IServiceConstants;
import org.eclipse.e4.ui.workbench.UIEvents;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.e4.ui.workbench.modeling.ISelectionListener;
import org.osgi.service.event.EventHandler;
public class SelectionAggregator {
static final String OUT_SELECTION = "org.eclipse.ui.output.selection"; //$NON-NLS-1$
static final String OUT_POST_SELECTION = "org.eclipse.ui.output.postSelection"; //$NON-NLS-1$
private ListenerList<ISelectionListener> genericListeners = new ListenerList<>();
private ListenerList<ISelectionListener> genericPostListeners = new ListenerList<>();
private Map<String, ListenerList<ISelectionListener>> targetedListeners = new HashMap<>();
private Map<String, ListenerList<ISelectionListener>> targetedPostListeners = new HashMap<>();
private Set<IEclipseContext> tracked = new HashSet<>();
private EventHandler eventHandler = event -> {
Object element = event.getProperty(UIEvents.EventTags.ELEMENT);
if (element instanceof MPart) {
MPart part = (MPart) element;
String partId = part.getElementId();
if (targetedListeners.containsKey(partId) || targetedPostListeners.containsKey(partId))
track(part);
}
};
private MPart activePart;
private IEclipseContext context;
private EPartService partService;
private IEventBroker eventBroker;
private Logger logger;
@Inject
SelectionAggregator(IEclipseContext context, EPartService partService,
IEventBroker eventBroker, Logger logger) {
super();
this.context = context;
this.partService = partService;
this.eventBroker = eventBroker;
this.logger = logger;
}
@PreDestroy
void preDestroy() {
genericListeners.clear();
genericPostListeners.clear();
targetedListeners.clear();
targetedPostListeners.clear();
eventBroker.unsubscribe(eventHandler);
}
@PostConstruct
void postConstruct() {
eventBroker.subscribe(UIEvents.Context.TOPIC_CONTEXT, eventHandler);
}
@Inject
void setPart(@Optional @Named(IServiceConstants.ACTIVE_PART) final MPart part) {
if (part == null) {
activePart = null;
context.set(IServiceConstants.ACTIVE_SELECTION, null);
} else if (activePart != part) {
activePart = part;
IEclipseContext partContext = part.getContext();
if (partContext.containsKey(OUT_POST_SELECTION)) {
Object selection = partContext.get(OUT_POST_SELECTION);
context.set(IServiceConstants.ACTIVE_SELECTION, selection);
} else if (partContext.containsKey(OUT_SELECTION)) {
Object selection = partContext.get(OUT_SELECTION);
context.set(IServiceConstants.ACTIVE_SELECTION, selection);
}
track(part);
}
}
private void notifyListeners(final MPart part, final Object selection) {
for (final ISelectionListener myListener : genericListeners) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
myListener.selectionChanged(part, selection);
}
@Override
public void handleException(Throwable exception) {
logger.error(exception);
}
});
}
notifyTargetedListeners(part, selection);
}
private void notifyTargetedListeners(final MPart part, final Object selection) {
String id = part.getElementId();
if (id != null) {
ListenerList<ISelectionListener> listenerList = targetedListeners.get(id);
if (listenerList != null) {
for (final ISelectionListener listener : listenerList) {
final ISelectionListener myListener = listener;
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
myListener.selectionChanged(part, selection);
}
@Override
public void handleException(Throwable exception) {
logger.error(exception);
}
});
}
}
}
}
private void notifyPostListeners(final MPart part, final Object selection) {
for (final ISelectionListener myListener : genericPostListeners) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
myListener.selectionChanged(part, selection);
}
@Override
public void handleException(Throwable exception) {
logger.error(exception);
}
});
}
notifyTargetedPostListeners(part, selection);
}
private void notifyTargetedPostListeners(final MPart part, final Object selection) {
String id = part.getElementId();
if (id != null) {
ListenerList<ISelectionListener> listenerList = targetedPostListeners.get(id);
if (listenerList != null) {
for (final ISelectionListener myListener : listenerList) {
SafeRunner.run(new ISafeRunnable() {
@Override
public void run() throws Exception {
myListener.selectionChanged(part, selection);
}
@Override
public void handleException(Throwable exception) {
logger.error(exception);
}
});
}
}
}
}
private void track(final MPart part) {
final IEclipseContext myContext = this.context;
IEclipseContext context = part.getContext();
if (context != null && tracked.add(context)) {
if (context instanceof EclipseContext) {
((EclipseContext) context).notifyOnDisposal(context1 -> tracked.remove(context1));
}
context.runAndTrack(new RunAndTrack() {
private boolean initial = true;
@Override
public boolean changed(IEclipseContext context) {
final Object selection = context.get(OUT_SELECTION);
if (initial) {
initial = false;
if (selection == null) {
return true;
}
}
if (activePart == part) {
myContext.set(IServiceConstants.ACTIVE_SELECTION, selection);
runExternalCode(() -> notifyListeners(part, selection));
} else {
runExternalCode(() -> notifyTargetedListeners(part, selection));
// we don't need to keep tracking non-active parts unless
// they have targeted listeners
String partId = part.getElementId();
boolean continueTracking = targetedListeners.containsKey(partId)
|| targetedPostListeners.containsKey(partId);
if (!continueTracking) {
tracked.remove(part.getContext());
}
return continueTracking;
}
return true;
}
});
context.runAndTrack(new RunAndTrack() {
private boolean initial = true;
@Override
public boolean changed(IEclipseContext context) {
final Object postSelection = context.get(OUT_POST_SELECTION);
if (initial) {
initial = false;
if (postSelection == null) {
return true;
}
}
if (activePart == part) {
runExternalCode(() -> notifyPostListeners(part, postSelection));
} else {
runExternalCode(() -> notifyTargetedPostListeners(part, postSelection));
// we don't need to keep tracking non-active parts unless
// they have targeted listeners
String partId = part.getElementId();
boolean continueTracking = targetedListeners.containsKey(partId)
|| targetedPostListeners.containsKey(partId);
if (!continueTracking) {
tracked.remove(part.getContext());
}
return continueTracking;
}
return true;
}
});
}
}
public Object getSelection() {
return context.get(IServiceConstants.ACTIVE_SELECTION);
}
public void addSelectionListener(ISelectionListener listener) {
genericListeners.add(listener);
}
public void addPostSelectionListener(ISelectionListener listener) {
genericPostListeners.add(listener);
}
public void removeSelectionListener(ISelectionListener listener) {
// we may have been destroyed already, see bug 310113
if (context != null) {
genericListeners.remove(listener);
}
}
public void removePostSelectionListener(ISelectionListener listener) {
// we may have been destroyed already, see bug 310113
if (context != null) {
genericPostListeners.remove(listener);
}
}
public void addSelectionListener(String partId, ISelectionListener listener) {
ListenerList<ISelectionListener> listeners = targetedListeners.get(partId);
if (listeners == null) {
listeners = new ListenerList<>();
targetedListeners.put(partId, listeners);
}
listeners.add(listener);
MPart part = partService.findPart(partId);
if (part != null)
track(part);
}
public void addPostSelectionListener(String partId, ISelectionListener listener) {
ListenerList<ISelectionListener> listeners = targetedPostListeners.get(partId);
if (listeners == null) {
listeners = new ListenerList<>();
targetedPostListeners.put(partId, listeners);
}
listeners.add(listener);
MPart part = partService.findPart(partId);
if (part != null)
track(part);
}
public void removeSelectionListener(String partId, ISelectionListener listener) {
// we may have been destroyed already, see bug 310113
if (context != null) {
ListenerList<ISelectionListener> listeners = targetedListeners.get(partId);
if (listeners != null) {
listeners.remove(listener);
}
}
}
public void removePostSelectionListener(String partId, ISelectionListener listener) {
// we may have been destroyed already, see bug 310113
if (context != null) {
ListenerList<ISelectionListener> listeners = targetedPostListeners.get(partId);
if (listeners != null) {
listeners.remove(listener);
}
}
}
public Object getSelection(String partId) {
MPart part = partService.findPart(partId);
if (part == null) {
return null;
}
IEclipseContext partContext = part.getContext();
if (partContext == null) {
return null;
}
return partContext.get(OUT_SELECTION);
}
}