| /******************************************************************************* |
| * Copyright (c) 2005, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Lars Vogel <Lars.Vogel@vogella.com> - Bug 472654 |
| *******************************************************************************/ |
| package org.eclipse.ui.internal.contexts; |
| |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import javax.inject.Inject; |
| import org.eclipse.core.commands.contexts.Context; |
| import org.eclipse.core.commands.contexts.ContextManager; |
| import org.eclipse.core.commands.contexts.IContextManagerListener; |
| import org.eclipse.core.expressions.EvaluationResult; |
| import org.eclipse.core.expressions.Expression; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.e4.core.commands.ExpressionContext; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.contexts.RunAndTrack; |
| import org.eclipse.e4.ui.services.EContextService; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.ISourceProvider; |
| import org.eclipse.ui.ISources; |
| import org.eclipse.ui.contexts.IContextActivation; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.internal.WorkbenchPlugin; |
| |
| /** |
| * <p> |
| * Provides services related to contexts in the Eclipse workbench. This provides |
| * access to contexts. |
| * </p> |
| * |
| * @since 3.1 |
| */ |
| public final class ContextService implements IContextService { |
| |
| private HashMap<IContextActivation, UpdateExpression> activationToRat = new HashMap<>(); |
| |
| /** |
| * The central authority for determining which context we should use. |
| */ |
| private final ContextAuthority contextAuthority; |
| |
| /** |
| * The context manager that supports this service. This value is never |
| * <code>null</code>. |
| */ |
| private ContextManager contextManager; |
| |
| @Inject |
| private EContextService contextService; |
| |
| @Inject |
| private IEclipseContext eclipseContext; |
| |
| /** |
| * The persistence class for this context service. |
| */ |
| private final ContextPersistence contextPersistence; |
| |
| /** |
| * Constructs a new instance of <code>ContextService</code> using a context |
| * manager. |
| * |
| * @param contextManager The context manager to use; must not be |
| * <code>null</code>. |
| */ |
| @Inject |
| public ContextService(final ContextManager contextManager) { |
| if (contextManager == null) { |
| throw new NullPointerException("Cannot create a context service with a null manager"); //$NON-NLS-1$ |
| } |
| this.contextManager = contextManager; |
| this.contextAuthority = new ContextAuthority(contextManager, this); |
| this.contextPersistence = new ContextPersistence(contextManager); |
| } |
| |
| @Override |
| public void deferUpdates(boolean defer) { |
| contextManager.deferUpdates(defer); |
| contextService.deferUpdates(defer); |
| } |
| |
| @Override |
| public IContextActivation activateContext(final String contextId) { |
| return activateContext(contextId, null); |
| } |
| |
| private class UpdateExpression extends RunAndTrack { |
| boolean updating = true; |
| |
| private String contextId; |
| private Expression expression; |
| |
| EvaluationResult cached = null; |
| |
| public UpdateExpression(String contextId, Expression expression) { |
| this.contextId = contextId; |
| this.expression = expression; |
| } |
| |
| @Override |
| public boolean changed(IEclipseContext context) { |
| if (!updating) { |
| return false; |
| } |
| ExpressionContext ctx = new ExpressionContext(eclipseContext); |
| try { |
| if (updating) { |
| EvaluationResult result = expression.evaluate(ctx); |
| if (cached != null && (cached == result |
| || (cached != EvaluationResult.FALSE && result != EvaluationResult.FALSE))) { |
| return updating; |
| } |
| if (result != EvaluationResult.FALSE) { |
| runExternalCode(() -> contextService.activateContext(contextId)); |
| } else if (cached != null) { |
| runExternalCode(() -> contextService.deactivateContext(contextId)); |
| } |
| cached = result; |
| } |
| } catch (CoreException e) { |
| // contextService.deactivateContext(contextId); |
| WorkbenchPlugin.log("Failed to update " + contextId, e); //$NON-NLS-1$ |
| } |
| return updating; |
| } |
| } |
| |
| @Override |
| public IContextActivation activateContext(final String contextId, final Expression expression) { |
| |
| final IContextActivation activation = new ContextActivation(contextId, expression, this); |
| contextAuthority.activateContext(activation); |
| if (expression == null) { |
| contextService.activateContext(contextId); |
| } else { |
| final UpdateExpression runnable = new UpdateExpression(contextId, expression); |
| activationToRat.put(activation, runnable); |
| eclipseContext.runAndTrack(runnable); |
| } |
| return activation; |
| } |
| |
| @Override |
| public IContextActivation activateContext(String contextId, Expression expression, boolean global) { |
| return activateContext(contextId, expression); |
| } |
| |
| @Override |
| public IContextActivation activateContext(final String contextId, final Expression expression, |
| final int sourcePriority) { |
| return activateContext(contextId, expression); |
| } |
| |
| @Override |
| public void addContextManagerListener(final IContextManagerListener listener) { |
| contextManager.addContextManagerListener(listener); |
| } |
| |
| @Override |
| public void addSourceProvider(final ISourceProvider provider) { |
| contextAuthority.addSourceProvider(provider); |
| } |
| |
| @Override |
| public void deactivateContext(final IContextActivation activation) { |
| if (activation != null && activation.getContextService() == this) { |
| final UpdateExpression rat = activationToRat.remove(activation); |
| if (rat != null) { |
| rat.updating = false; |
| if (rat.cached != null && rat.cached != EvaluationResult.FALSE) { |
| contextService.deactivateContext(activation.getContextId()); |
| } |
| } else { |
| contextService.deactivateContext(activation.getContextId()); |
| } |
| contextAuthority.deactivateContext(activation); |
| } |
| } |
| |
| @Override |
| public void deactivateContexts(final Collection activations) { |
| try { |
| deferUpdates(true); |
| final Iterator<?> activationItr = activations.iterator(); |
| while (activationItr.hasNext()) { |
| final IContextActivation activation = (IContextActivation) activationItr.next(); |
| deactivateContext(activation); |
| } |
| } finally { |
| deferUpdates(false); |
| } |
| } |
| |
| @Override |
| public void dispose() { |
| contextPersistence.dispose(); |
| contextAuthority.dispose(); |
| } |
| |
| @Override |
| public Collection getActiveContextIds() { |
| return contextService.getActiveContextIds(); |
| } |
| |
| @Override |
| public Context getContext(final String contextId) { |
| return contextService.getContext(contextId); |
| } |
| |
| @Override |
| public Collection getDefinedContextIds() { |
| return contextManager.getDefinedContextIds(); |
| } |
| |
| @Override |
| public Context[] getDefinedContexts() { |
| return contextManager.getDefinedContexts(); |
| } |
| |
| @Override |
| public int getShellType(final Shell shell) { |
| return contextAuthority.getShellType(shell); |
| } |
| |
| @Override |
| public void readRegistry() { |
| // contextPersistence.read(); |
| } |
| |
| @Override |
| public boolean registerShell(final Shell shell, final int type) { |
| return contextAuthority.registerShell(shell, type); |
| } |
| |
| @Override |
| public void removeContextManagerListener(final IContextManagerListener listener) { |
| contextManager.removeContextManagerListener(listener); |
| } |
| |
| @Override |
| public void removeSourceProvider(final ISourceProvider provider) { |
| contextAuthority.removeSourceProvider(provider); |
| } |
| |
| @Override |
| public boolean unregisterShell(final Shell shell) { |
| return contextAuthority.unregisterShell(shell); |
| } |
| |
| /** |
| * <p> |
| * Bug 95792. A mechanism by which the key binding architecture can force an |
| * update of the contexts (based on the active shell) before trying to execute a |
| * command. This mechanism is required for GTK+ only. |
| * </p> |
| * <p> |
| * DO NOT CALL THIS METHOD. |
| * </p> |
| */ |
| public void updateShellKludge() { |
| contextAuthority.updateShellKludge(); |
| } |
| |
| /** |
| * <p> |
| * Bug 95792. A mechanism by which the key binding architecture can force an |
| * update of the contexts (based on the active shell) before trying to execute a |
| * command. This mechanism is required for GTK+ only. |
| * </p> |
| * <p> |
| * DO NOT CALL THIS METHOD. |
| * </p> |
| * |
| * @param shell The shell that should be considered active; must not be |
| * <code>null</code>. |
| */ |
| public void updateShellKludge(final Shell shell) { |
| final Shell currentActiveShell = contextAuthority.getActiveShell(); |
| if (currentActiveShell != shell) { |
| contextAuthority.sourceChanged(ISources.ACTIVE_SHELL, ISources.ACTIVE_SHELL_NAME, shell); |
| } |
| } |
| } |