blob: 32f694fd3453a58247277ae259c0193b5a182093 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2006 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.menus;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.window.Window;
import org.eclipse.ui.internal.services.ExpressionAuthority;
/**
* <p>
* A central authority for deciding visibility of menu elements. This authority
* listens to a variety of incoming sources, and updates the underlying menu
* manager if changes occur. It only updates those menu elements that are
* showing, and listens to the menu manager to determine which menu elements are
* currently visible.
* </p>
* <p>
* This class is not intended for use outside of the
* <code>org.eclipse.ui.workbench</code> plug-in.
* </p>
*
* @since 3.2
*/
final class MenuAuthority extends ExpressionAuthority {
/**
* This is a map of menu element contributions (<code>Collection</code>
* of <code>IMenuContribution</code>) sorted by identifier (<code>MenuElement</code>).
* If there is only one contribution for a menu element, then the
* <code>Collection</code> is replaced by a <code>IMenuContribution</code>.
* If there is no contribution, the entry should be removed entirely.
*/
private final Map menuContributionsByElement = new HashMap();
/**
* A bucket sort of the contributed menu elements based on source priority.
* This only includes the items that are currently showing within the
* workbench. Each contribution will appear only once per set, but may
* appear in multiple sets. If no contributions are defined for a particular
* priority level, then the array at that index will only contain
* <code>null</code>.
*/
private final Set[] showingContributionsBySourcePriority = new Set[33];
/**
* The window for which this authority applies. This is used for determining
* visibility of particular menu elements.
*/
private final Window window;
/**
* Constructs a new instance of <code>MenuAuthority</code>.
*
* @param window
* The window to use when calling
* {@link MenuElement#setVisible(Window, boolean)}; may be
* <code>null</code>.
*/
MenuAuthority(final Window window) {
this.window = window;
}
/**
* Contributes a menu element to the workbench. This will add it to a master
* list.
*
* @param contribution
* The contribution; must not be <code>null</code>.
*/
final void contributeMenu(final IMenuContribution contribution) {
// First we update the menuContributionsByElement map.
final MenuElement element = contribution.getMenuElement();
final Object value = menuContributionsByElement.get(element);
if (value instanceof Collection) {
final Collection menuContributions = (Collection) value;
if (!menuContributions.contains(contribution)) {
menuContributions.add(contribution);
}
} else if (value instanceof IMenuContribution) {
if (value != contribution) {
final Collection menuContributions = new ArrayList(2);
menuContributions.add(value);
menuContributions.add(contribution);
menuContributionsByElement.put(element, menuContributions);
}
} else {
menuContributionsByElement.put(element, contribution);
}
// Next we update the source priority bucket sort of activations.
if (contribution.getMenuElement().isShowing(window)) {
final int sourcePriority = contribution.getSourcePriority();
for (int i = 1; i <= 32; i++) {
if ((sourcePriority & (1 << i)) != 0) {
Set contributions = showingContributionsBySourcePriority[i];
if (contributions == null) {
contributions = new HashSet(1);
showingContributionsBySourcePriority[i] = contributions;
}
contributions.add(contribution);
}
}
}
}
/**
* Removes a contribution from the workbench. This will remove it from the
* master list, and update as appropriate.
*
* @param contribution
* The contribution; must not be <code>null</code>.
*/
final void removeContribution(final IMenuContribution contribution) {
// First we update the menuContributionByElement map.
final MenuElement element = contribution.getMenuElement();
final Object value = menuContributionsByElement.get(element);
if (value instanceof Collection) {
final Collection menuContributions = (Collection) value;
if (menuContributions.contains(contribution)) {
menuContributions.remove(contribution);
if (menuContributions.isEmpty()) {
menuContributionsByElement.remove(element);
} else if (menuContributions.size() == 1) {
final IMenuContribution remainingContribution = (IMenuContribution) menuContributions
.iterator().next();
menuContributionsByElement.put(element,
remainingContribution);
}
}
} else if (value instanceof IMenuContribution) {
if (value == contribution) {
menuContributionsByElement.remove(element);
}
}
// Next we update the source priority bucket sort of activations.
if (element.isShowing(window)) {
final int sourcePriority = contribution.getSourcePriority();
for (int i = 1; i <= 32; i++) {
if ((sourcePriority & (1 << i)) != 0) {
final Set contributions = showingContributionsBySourcePriority[i];
if (contributions == null) {
continue;
}
contributions.remove(contribution);
if (contributions.isEmpty()) {
showingContributionsBySourcePriority[i] = null;
}
}
}
}
}
/**
* Carries out the actual source change notification. It assumed that by the
* time this method is called, <code>getEvaluationContext()</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) {
/*
* In this first phase, we cycle through all of the contributions that
* could have potentially changed. Each such contribution is added to a
* set for future processing. We add it to a set so that we avoid
* handling any individual contribution more than once.
*/
final Set contributionsToRecompute = new HashSet();
for (int i = 1; i <= 32; i++) {
if ((sourcePriority & (1 << i)) != 0) {
final Collection contributions = showingContributionsBySourcePriority[i];
if (contributions != null) {
final Iterator contributionItr = contributions.iterator();
while (contributionItr.hasNext()) {
contributionsToRecompute.add(contributionItr.next());
}
}
}
}
/*
* For every contribution, we recompute its state, and check whether it
* has changed. If it has changed, then we take note of the menu element
* so we can update the menu element later.
*/
final Iterator contributionItr = contributionsToRecompute.iterator();
while (contributionItr.hasNext()) {
final IMenuContribution contribution = (IMenuContribution) contributionItr
.next();
final boolean currentlyVisible = evaluate(contribution);
contribution.clearResult();
final boolean newVisible = evaluate(contribution);
if (newVisible != currentlyVisible) {
contribution.getMenuElement().setVisible(window, newVisible);
}
}
}
}