| /******************************************************************************* |
| * Copyright (c) 2005, 2008 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 |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.internal.contexts; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.WeakHashMap; |
| |
| import org.eclipse.core.commands.contexts.ContextManager; |
| import org.eclipse.core.commands.util.Tracing; |
| import org.eclipse.core.expressions.Expression; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.ui.ActiveShellExpression; |
| import org.eclipse.ui.ISources; |
| import org.eclipse.ui.contexts.IContextActivation; |
| import org.eclipse.ui.contexts.IContextService; |
| import org.eclipse.ui.internal.misc.Policy; |
| import org.eclipse.ui.internal.services.ExpressionAuthority; |
| |
| /** |
| * <p> |
| * A central authority for deciding activation of contexts. This authority |
| * listens to a variety of incoming sources, and updates the underlying context |
| * manager if changes occur. |
| * </p> |
| * |
| * @since 3.1 |
| */ |
| public final class ContextAuthority extends ExpressionAuthority { |
| |
| |
| /** |
| * The default size of the set containing the activations to recompute. This |
| * is more than enough to cover the average case. |
| */ |
| private static final int ACTIVATIONS_TO_RECOMPUTE_SIZE = 4; |
| |
| /** |
| * Whether the context authority should kick into debugging mode. This |
| * causes the unresolvable handler conflicts to be printed to the console. |
| */ |
| private static final boolean DEBUG = Policy.DEBUG_CONTEXTS; |
| |
| /** |
| * Whether the performance information should be printed about the |
| * performance of the context authority. |
| */ |
| private static final boolean DEBUG_PERFORMANCE = Policy.DEBUG_CONTEXTS_PERFORMANCE; |
| |
| /** |
| * The name of the data tag containing the dispose listener information. |
| */ |
| private static final String DISPOSE_LISTENER = "org.eclipse.ui.internal.contexts.ContextAuthority"; //$NON-NLS-1$ |
| |
| /** |
| * The component name to print when displaying tracing information. |
| */ |
| private static final String TRACING_COMPONENT = "CONTEXTS"; //$NON-NLS-1$ |
| |
| /** |
| * A bucket sort of the context activations based on source priority. Each |
| * activation will appear only once per set, but may appear in multiple |
| * sets. If no activations are defined for a particular priority level, then |
| * the array at that index will only contain <code>null</code>. |
| */ |
| private final Set[] activationsBySourcePriority = new Set[33]; |
| |
| /** |
| * This is a map of context activations (<code>Collection</code> of |
| * <code>IContextActivation</code>) sorted by context identifier (<code>String</code>). |
| * If there is only one context activation for a context, then the |
| * <code>Collection</code> is replaced by a |
| * <code>IContextActivation</code>. If there is no activation, the entry |
| * should be removed entirely. |
| */ |
| private final Map contextActivationsByContextId = new HashMap(); |
| |
| /** |
| * The context manager that should be updated when the contexts are |
| * changing. |
| */ |
| private final ContextManager contextManager; |
| |
| /** |
| * The context service that should be used for authority-managed |
| * shell-related contexts. This value is never <code>null</code>. |
| */ |
| private final IContextService contextService; |
| |
| /** |
| * This is a map of shell to a list of activations. When a shell is |
| * registered, it is added to this map with the list of activation that |
| * should be submitted when the shell is active. When the shell is |
| * deactivated, this same list should be withdrawn. A shell is removed from |
| * this map using the {@link #unregisterShell(Shell)}method. This value may |
| * be empty, but is never <code>null</code>. The <code>null</code> key |
| * is reserved for active shells that have not been registered but have a |
| * parent (i.e., default dialog service). |
| */ |
| private final Map registeredWindows = new WeakHashMap(); |
| |
| /** |
| * Constructs a new instance of <code>ContextAuthority</code>. |
| * |
| * @param contextManager |
| * The context manager from which contexts can be retrieved (to |
| * update their active state); must not be <code>null</code>. |
| * @param contextService |
| * The workbench context service for which this authority is |
| * acting. This allows the authority to manage shell-specific |
| * contexts. This value must not be <code>null</code>. |
| */ |
| ContextAuthority(final ContextManager contextManager, |
| final IContextService contextService) { |
| if (contextManager == null) { |
| throw new NullPointerException( |
| "The context authority needs a context manager"); //$NON-NLS-1$ |
| } |
| if (contextService == null) { |
| throw new NullPointerException( |
| "The context authority needs an evaluation context"); //$NON-NLS-1$ |
| } |
| |
| this.contextManager = contextManager; |
| this.contextService = contextService; |
| } |
| |
| /** |
| * Activates a context on the workbench. This will add it to a master list. |
| * |
| * @param activation |
| * The activation; must not be <code>null</code>. |
| */ |
| final void activateContext(final IContextActivation activation) { |
| // First we update the contextActivationsByContextId map. |
| final String contextId = activation.getContextId(); |
| final Object value = contextActivationsByContextId.get(contextId); |
| if (value instanceof Collection) { |
| final Collection contextActivations = (Collection) value; |
| if (!contextActivations.contains(activation)) { |
| contextActivations.add(activation); |
| updateContext(contextId, containsActive(contextActivations)); |
| } |
| } else if (value instanceof IContextActivation) { |
| if (value != activation) { |
| final Collection contextActivations = new ArrayList(2); |
| contextActivations.add(value); |
| contextActivations.add(activation); |
| contextActivationsByContextId |
| .put(contextId, contextActivations); |
| updateContext(contextId, containsActive(contextActivations)); |
| } |
| } else { |
| contextActivationsByContextId.put(contextId, activation); |
| updateContext(contextId, evaluate(activation)); |
| } |
| |
| // Next we update the source priority bucket sort of activations. |
| final int sourcePriority = activation.getSourcePriority(); |
| for (int i = 1; i <= 32; i++) { |
| if ((sourcePriority & (1 << i)) != 0) { |
| Set activations = activationsBySourcePriority[i]; |
| if (activations == null) { |
| activations = new HashSet(1); |
| activationsBySourcePriority[i] = activations; |
| } |
| activations.add(activation); |
| } |
| } |
| } |
| |
| /** |
| * Checks whether the new active shell is registered. If it is already |
| * registered, then it does no work. If it is not registered, then it checks |
| * what type of contexts the shell should have by default. This is |
| * determined by parenting. A shell with no parent receives no contexts. A |
| * shell with a parent, receives the dialog contexts. |
| * |
| * @param newShell |
| * The newly active shell; may be <code>null</code> or |
| * disposed. |
| * @param oldShell |
| * The previously active shell; may be <code>null</code> or |
| * disposed. |
| */ |
| private final void checkWindowType(final Shell newShell, |
| final Shell oldShell) { |
| /* |
| * If the previous active shell was recognized as a dialog by default, |
| * then remove its submissions. |
| */ |
| Collection oldActivations = (Collection) registeredWindows |
| .get(oldShell); |
| if (oldActivations == null) { |
| /* |
| * The old shell wasn't registered. So, we need to check if it was |
| * considered a dialog by default. |
| */ |
| oldActivations = (Collection) registeredWindows.get(null); |
| if (oldActivations != null) { |
| final Iterator oldActivationItr = oldActivations.iterator(); |
| while (oldActivationItr.hasNext()) { |
| final IContextActivation activation = (IContextActivation) oldActivationItr |
| .next(); |
| deactivateContext(activation); |
| } |
| } |
| } |
| |
| /* |
| * If the new active shell is recognized as a dialog by default, then |
| * create some submissions, remember them, and submit them for |
| * processing. |
| */ |
| if ((newShell != null) && (!newShell.isDisposed())) { |
| final Collection newActivations; |
| |
| if ((newShell.getParent() != null) |
| && (registeredWindows.get(newShell) == null)) { |
| // This is a dialog by default. |
| newActivations = new ArrayList(); |
| final Expression expression = new ActiveShellExpression( |
| newShell); |
| final IContextActivation dialogWindowActivation = new ContextActivation( |
| IContextService.CONTEXT_ID_DIALOG_AND_WINDOW, |
| expression, contextService); |
| activateContext(dialogWindowActivation); |
| newActivations.add(dialogWindowActivation); |
| final IContextActivation dialogActivation = new ContextActivation( |
| IContextService.CONTEXT_ID_DIALOG, expression, |
| contextService); |
| activateContext(dialogActivation); |
| newActivations.add(dialogActivation); |
| registeredWindows.put(null, newActivations); |
| |
| /* |
| * Make sure the submissions will be removed in event of |
| * disposal. This is really just a paranoid check. The |
| * "oldSubmissions" code above should take care of this. |
| */ |
| newShell.addDisposeListener(new DisposeListener() { |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) |
| */ |
| public void widgetDisposed(DisposeEvent e) { |
| registeredWindows.remove(null); |
| if (!newShell.isDisposed()) { |
| newShell.removeDisposeListener(this); |
| } |
| |
| /* |
| * In the case where a dispose has happened, we are |
| * expecting an activation event to arrive at some point |
| * in the future. If we process the submissions now, |
| * then we will update the activeShell before |
| * checkWindowType is called. This means that dialogs |
| * won't be recognized as dialogs. |
| */ |
| final Iterator newActivationItr = newActivations |
| .iterator(); |
| while (newActivationItr.hasNext()) { |
| deactivateContext((IContextActivation) newActivationItr |
| .next()); |
| } |
| } |
| }); |
| |
| } else { |
| // Shells that are not dialogs by default must register. |
| newActivations = null; |
| |
| } |
| } |
| } |
| |
| /** |
| * Returns a subset of the given <code>activations</code> containing only |
| * those that are active |
| * |
| * @param activations |
| * The activations to trim; must not be <code>null</code>, but |
| * may be empty. |
| * @return <code>true</code> if there is at least one active context; |
| * <code>false</code> otherwise. |
| */ |
| private final boolean containsActive(final Collection activations) { |
| final Iterator activationItr = activations.iterator(); |
| while (activationItr.hasNext()) { |
| final IContextActivation activation = (IContextActivation) activationItr |
| .next(); |
| if (evaluate(activation)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Removes an activation for a context on the workbench. This will remove it |
| * from the master list, and update the appropriate context, if necessary. |
| * |
| * @param activation |
| * The activation; must not be <code>null</code>. |
| */ |
| final void deactivateContext(final IContextActivation activation) { |
| // First we update the handlerActivationsByCommandId map. |
| final String contextId = activation.getContextId(); |
| final Object value = contextActivationsByContextId.get(contextId); |
| if (value instanceof Collection) { |
| final Collection contextActivations = (Collection) value; |
| if (contextActivations.contains(activation)) { |
| contextActivations.remove(activation); |
| if (contextActivations.isEmpty()) { |
| contextActivationsByContextId.remove(contextId); |
| updateContext(contextId, false); |
| |
| } else if (contextActivations.size() == 1) { |
| final IContextActivation remainingActivation = (IContextActivation) contextActivations |
| .iterator().next(); |
| contextActivationsByContextId.put(contextId, |
| remainingActivation); |
| updateContext(contextId, evaluate(remainingActivation)); |
| |
| } else { |
| updateContext(contextId, containsActive(contextActivations)); |
| } |
| } |
| } else if (value instanceof IContextActivation) { |
| if (value == activation) { |
| contextActivationsByContextId.remove(contextId); |
| updateContext(contextId, false); |
| } |
| } |
| |
| // Next we update the source priority bucket sort of activations. |
| final int sourcePriority = activation.getSourcePriority(); |
| for (int i = 1; i <= 32; i++) { |
| if ((sourcePriority & (1 << i)) != 0) { |
| final Set activations = activationsBySourcePriority[i]; |
| if (activations == null) { |
| continue; |
| } |
| activations.remove(activation); |
| if (activations.isEmpty()) { |
| activationsBySourcePriority[i] = null; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the currently active shell. |
| * |
| * @return The currently active shell; may be <code>null</code>. |
| */ |
| final Shell getActiveShell() { |
| return (Shell) getVariable(ISources.ACTIVE_SHELL_NAME); |
| } |
| |
| /** |
| * Returns the shell type for the given shell. |
| * |
| * @param shell |
| * The shell for which the type should be determined. If this |
| * value is <code>null</code>, then |
| * <code>IWorkbenchContextSupport.TYPE_NONE</code> is returned. |
| * @return <code>IWorkbenchContextSupport.TYPE_WINDOW</code>, |
| * <code>IWorkbenchContextSupport.TYPE_DIALOG</code>, or |
| * <code>IWorkbenchContextSupport.TYPE_NONE</code>. |
| */ |
| public final int getShellType(final Shell shell) { |
| // If the shell is null, then return none. |
| if (shell == null) { |
| return IContextService.TYPE_NONE; |
| } |
| |
| final Collection activations = (Collection) registeredWindows |
| .get(shell); |
| if (activations != null) { |
| // The shell is registered, so check what type it was registered as. |
| if (activations.isEmpty()) { |
| // It was registered as none. |
| return IContextService.TYPE_NONE; |
| } |
| |
| // Look for the right type of context id. |
| final Iterator activationItr = activations.iterator(); |
| while (activationItr.hasNext()) { |
| final IContextActivation activation = (IContextActivation) activationItr |
| .next(); |
| final String contextId = activation.getContextId(); |
| if (contextId == IContextService.CONTEXT_ID_DIALOG) { |
| return IContextService.TYPE_DIALOG; |
| } else if (contextId == IContextService.CONTEXT_ID_WINDOW) { |
| return IContextService.TYPE_WINDOW; |
| } |
| } |
| |
| // This shouldn't be possible. |
| Assert |
| .isTrue( |
| false, |
| "A registered shell should have at least one submission matching TYPE_WINDOW or TYPE_DIALOG"); //$NON-NLS-1$ |
| return IContextService.TYPE_NONE; // not reachable |
| |
| } else if (shell.getParent() != null) { |
| /* |
| * The shell is not registered, but it has a parent. It is therefore |
| * considered a dialog by default. |
| */ |
| return IContextService.TYPE_DIALOG; |
| |
| } else { |
| /* |
| * The shell is not registered, but has no parent. It gets no key |
| * bindings. |
| */ |
| return IContextService.TYPE_NONE; |
| } |
| } |
| |
| /** |
| * <p> |
| * Registers a shell to automatically promote or demote some basic types of |
| * contexts. The "In Dialogs" and "In Windows" contexts are provided by the |
| * system. This a convenience method to ensure that these contexts are |
| * promoted when the given is shell is active. |
| * </p> |
| * <p> |
| * If a shell is registered as a window, then the "In Windows" context is |
| * enabled when that shell is active. If a shell is registered as a dialog -- |
| * or is not registered, but has a parent shell -- then the "In Dialogs" |
| * context is enabled when that shell is active. If the shell is registered |
| * as none -- or is not registered, but has no parent shell -- then the |
| * neither of the contexts will be enabled (by us -- someone else can always |
| * enabled them). |
| * </p> |
| * <p> |
| * If the provided shell has already been registered, then this method will |
| * change the registration. |
| * </p> |
| * |
| * @param shell |
| * The shell to register for key bindings; must not be |
| * <code>null</code>. |
| * @param type |
| * The type of shell being registered. This value must be one of |
| * the constants given in this interface. |
| * |
| * @return <code>true</code> if the shell had already been registered |
| * (i.e., the registration has changed); <code>false</code> |
| * otherwise. |
| */ |
| public final boolean registerShell(final Shell shell, final int type) { |
| // We do not allow null shell registration. It is reserved. |
| if (shell == null) { |
| throw new NullPointerException("The shell was null"); //$NON-NLS-1$ |
| } |
| |
| // Debugging output |
| if (DEBUG) { |
| final StringBuffer buffer = new StringBuffer("register shell '"); //$NON-NLS-1$ |
| buffer.append(shell); |
| buffer.append("' as "); //$NON-NLS-1$ |
| switch (type) { |
| case IContextService.TYPE_DIALOG: |
| buffer.append("dialog"); //$NON-NLS-1$ |
| break; |
| case IContextService.TYPE_WINDOW: |
| buffer.append("window"); //$NON-NLS-1$ |
| break; |
| case IContextService.TYPE_NONE: |
| buffer.append("none"); //$NON-NLS-1$ |
| break; |
| default: |
| buffer.append("unknown"); //$NON-NLS-1$ |
| break; |
| } |
| Tracing.printTrace(TRACING_COMPONENT, buffer.toString()); |
| } |
| |
| // Build the list of submissions. |
| final List activations = new ArrayList(); |
| Expression expression; |
| IContextActivation dialogWindowActivation; |
| switch (type) { |
| case IContextService.TYPE_DIALOG: |
| expression = new ActiveShellExpression(shell); |
| dialogWindowActivation = new ContextActivation( |
| IContextService.CONTEXT_ID_DIALOG_AND_WINDOW, expression, |
| contextService); |
| activateContext(dialogWindowActivation); |
| activations.add(dialogWindowActivation); |
| final IContextActivation dialogActivation = new ContextActivation( |
| IContextService.CONTEXT_ID_DIALOG, expression, |
| contextService); |
| activateContext(dialogActivation); |
| activations.add(dialogActivation); |
| break; |
| case IContextService.TYPE_NONE: |
| break; |
| case IContextService.TYPE_WINDOW: |
| expression = new ActiveShellExpression(shell); |
| dialogWindowActivation = new ContextActivation( |
| IContextService.CONTEXT_ID_DIALOG_AND_WINDOW, expression, |
| contextService); |
| activateContext(dialogWindowActivation); |
| activations.add(dialogWindowActivation); |
| final IContextActivation windowActivation = new ContextActivation( |
| IContextService.CONTEXT_ID_WINDOW, expression, |
| contextService); |
| activateContext(windowActivation); |
| activations.add(windowActivation); |
| break; |
| default: |
| throw new IllegalArgumentException("The type is not recognized: " //$NON-NLS-1$ |
| + type); |
| } |
| |
| // Check to see if the activations are already present. |
| boolean returnValue = false; |
| final Collection previousActivations = (Collection) registeredWindows |
| .get(shell); |
| if (previousActivations != null) { |
| returnValue = true; |
| final Iterator previousActivationItr = previousActivations |
| .iterator(); |
| while (previousActivationItr.hasNext()) { |
| final IContextActivation activation = (IContextActivation) previousActivationItr |
| .next(); |
| deactivateContext(activation); |
| } |
| } |
| |
| // Add the new submissions, and force some reprocessing to occur. |
| registeredWindows.put(shell, activations); |
| |
| /* |
| * Remember the dispose listener so that we can remove it later if we |
| * unregister the shell. |
| */ |
| final DisposeListener shellDisposeListener = new DisposeListener() { |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) |
| */ |
| public void widgetDisposed(DisposeEvent e) { |
| registeredWindows.remove(shell); |
| if (!shell.isDisposed()) { |
| shell.removeDisposeListener(this); |
| } |
| |
| /* |
| * In the case where a dispose has happened, we are expecting an |
| * activation event to arrive at some point in the future. If we |
| * process the submissions now, then we will update the |
| * activeShell before checkWindowType is called. This means that |
| * dialogs won't be recognized as dialogs. |
| */ |
| final Iterator activationItr = activations.iterator(); |
| while (activationItr.hasNext()) { |
| deactivateContext((IContextActivation) activationItr.next()); |
| } |
| } |
| }; |
| |
| // Make sure the submissions will be removed in event of disposal. |
| shell.addDisposeListener(shellDisposeListener); |
| shell.setData(DISPOSE_LISTENER, shellDisposeListener); |
| |
| return returnValue; |
| } |
| |
| /** |
| * Carries out the actual source change notification. It assumed that by the |
| * time this method is called, <code>context</code> is up-to-date with the |
| * current state of the application. |
| * |
| * @param sourcePriority |
| * A bit mask of all the source priorities that have changed. |
| */ |
| protected final void sourceChanged(final int sourcePriority) { |
| // If tracing, then track how long it takes to process the activations. |
| long startTime = 0L; |
| if (DEBUG_PERFORMANCE) { |
| startTime = System.currentTimeMillis(); |
| } |
| |
| /* |
| * In this first phase, we cycle through all of the activations that |
| * could have potentially changed. Each such activation is added to a |
| * set for future processing. We add it to a set so that we avoid |
| * handling any individual activation more than once. |
| */ |
| final Set activationsToRecompute = new HashSet( |
| ACTIVATIONS_TO_RECOMPUTE_SIZE); |
| for (int i = 1; i <= 32; i++) { |
| if ((sourcePriority & (1 << i)) != 0) { |
| final Collection activations = activationsBySourcePriority[i]; |
| if (activations != null) { |
| final Iterator activationItr = activations.iterator(); |
| while (activationItr.hasNext()) { |
| activationsToRecompute.add(activationItr.next()); |
| } |
| } |
| } |
| } |
| |
| /* |
| * For every activation, we recompute its active state, and check |
| * whether it has changed. If it has changed, then we take note of the |
| * context identifier so we can update the context later. |
| */ |
| final Collection changedContextIds = new ArrayList( |
| activationsToRecompute.size()); |
| final Iterator activationItr = activationsToRecompute.iterator(); |
| while (activationItr.hasNext()) { |
| final IContextActivation activation = (IContextActivation) activationItr |
| .next(); |
| final boolean currentActive = evaluate(activation); |
| activation.clearResult(); |
| final boolean newActive = evaluate(activation); |
| if (newActive != currentActive) { |
| changedContextIds.add(activation.getContextId()); |
| } |
| } |
| |
| try { |
| contextManager.deferUpdates(true); |
| /* |
| * For every context identifier with a changed activation, we |
| * resolve conflicts and trigger an update. |
| */ |
| final Iterator changedContextIdItr = changedContextIds.iterator(); |
| while (changedContextIdItr.hasNext()) { |
| final String contextId = (String) changedContextIdItr.next(); |
| final Object value = contextActivationsByContextId |
| .get(contextId); |
| if (value instanceof IContextActivation) { |
| final IContextActivation activation = (IContextActivation) value; |
| updateContext(contextId, evaluate(activation)); |
| } else if (value instanceof Collection) { |
| updateContext(contextId, containsActive((Collection) value)); |
| } else { |
| updateContext(contextId, false); |
| } |
| } |
| } finally { |
| contextManager.deferUpdates(false); |
| } |
| |
| // If tracing performance, then print the results. |
| if (DEBUG_PERFORMANCE) { |
| final long elapsedTime = System.currentTimeMillis() - startTime; |
| final int size = activationsToRecompute.size(); |
| if (size > 0) { |
| Tracing.printTrace(TRACING_COMPONENT, size |
| + " activations recomputed in " + elapsedTime + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| |
| /** |
| * <p> |
| * Unregisters a shell that was previously registered. After this method |
| * completes, the shell will be treated as if it had never been registered |
| * at all. If you have registered a shell, you should ensure that this |
| * method is called when the shell is disposed. Otherwise, a potential |
| * memory leak will exist. |
| * </p> |
| * <p> |
| * If the shell was never registered, or if the shell is <code>null</code>, |
| * then this method returns <code>false</code> and does nothing. |
| * |
| * @param shell |
| * The shell to be unregistered; does nothing if this value is |
| * <code>null</code>. |
| * |
| * @return <code>true</code> if the shell had been registered; |
| * <code>false</code> otherwise. |
| */ |
| public final boolean unregisterShell(final Shell shell) { |
| // Don't allow this method to play with the special null slot. |
| if (shell == null) { |
| return false; |
| } |
| |
| /* |
| * If we're unregistering the shell but we're not about to dispose it, |
| * then we'll end up leaking the DisposeListener unless we remove it |
| * here. |
| */ |
| if (!shell.isDisposed()) { |
| final DisposeListener oldListener = (DisposeListener) shell |
| .getData(DISPOSE_LISTENER); |
| if (oldListener != null) { |
| shell.removeDisposeListener(oldListener); |
| } |
| } |
| |
| Collection previousActivations = (Collection) registeredWindows |
| .get(shell); |
| if (previousActivations != null) { |
| registeredWindows.remove(shell); |
| |
| final Iterator previousActivationItr = previousActivations |
| .iterator(); |
| while (previousActivationItr.hasNext()) { |
| final IContextActivation activation = (IContextActivation) previousActivationItr |
| .next(); |
| deactivateContext(activation); |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Updates the context with the given context activation. |
| * |
| * @param contextId |
| * The identifier of the context which should be updated; must |
| * not be <code>null</code>. |
| * @param active |
| * Whether the context should be active; <code>false</code> |
| * otherwise. |
| */ |
| private final void updateContext(final String contextId, |
| final boolean active) { |
| if (active) { |
| contextManager.addActiveContext(contextId); |
| } else { |
| contextManager.removeActiveContext(contextId); |
| } |
| } |
| |
| /** |
| * Updates this authority's evaluation context. If the changed variable is |
| * the <code>ISources.ACTIVE_SHELL_NAME</code> variable, then this also |
| * triggers an update of the shell-specific contexts. For example, if a |
| * dialog becomes active, then the dialog context will be activated by this |
| * method. |
| * |
| * @param name |
| * The name of the variable to update; must not be |
| * <code>null</code>. |
| * @param value |
| * The new value of the variable. If this value is |
| * <code>null</code>, then the variable is removed. |
| */ |
| protected final void updateEvaluationContext(final String name, |
| final Object value) { |
| /* |
| * Bug 84056. If we update the active workbench window, then we risk |
| * falling back to that shell when the active shell has registered as |
| * "none". |
| */ |
| if ((name != null) |
| && (!ISources.ACTIVE_WORKBENCH_WINDOW_SHELL_NAME.equals(name))) { |
| /* |
| * We need to track shell activation ourselves, as some special |
| * contexts are automatically activated in response to different |
| * types of shells becoming active. |
| */ |
| if (ISources.ACTIVE_SHELL_NAME.equals(name)) { |
| checkWindowType((Shell) value, |
| (Shell) getVariable(ISources.ACTIVE_SHELL_NAME)); |
| } |
| |
| // Update the evaluation context itself. |
| changeVariable(name, value); |
| } |
| } |
| |
| /** |
| * <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> |
| */ |
| final void updateShellKludge() { |
| updateCurrentState(); |
| sourceChanged(ISources.ACTIVE_SHELL); |
| } |
| } |