blob: 54024dd4d58946892761b8e454c15b65ebf44f6c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2005 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.jface.action;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jface.util.Policy;
/**
* Abstract base class for all contribution managers, and standard implementation
* of <code>IContributionManager</code>. This class provides functionality
* common across the specific managers defined by this framework.
* <p>
* This class maintains a list of contribution items and a dirty flag, both as
* internal state. In addition to providing implementations of most
* <code>IContributionManager</code> methods, this class automatically
* coalesces adjacent separators, hides beginning and ending separators,
* and deals with dynamically changing sets of contributions. When the set
* of contributions does change dynamically, the changes are propagated
* to the control via the <code>update</code> method, which subclasses
* must implement.
* </p>
* <p>
* Note: A <code>ContributionItem</code> cannot be shared between different
* <code>ContributionManager</code>s.
* </p>
*/
public abstract class ContributionManager implements IContributionManager {
// Internal debug flag.
// protected static final boolean DEBUG = false;
/**
* The list of contribution items.
*/
private List contributions = new ArrayList();
/**
* Indicates whether the widgets are in sync with the contributions.
*/
private boolean isDirty = true;
/**
* Number of dynamic contribution items.
*/
private int dynamicItems = 0;
/**
* The overrides for items of this manager
*/
private IContributionManagerOverrides overrides;
/**
* Creates a new contribution manager.
*/
protected ContributionManager() {
// Do nothing.
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void add(IAction action) {
add(new ActionContributionItem(action));
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void add(IContributionItem item) {
if (allowItem(item)) {
contributions.add(item);
itemAdded(item);
}
}
/**
* Adds a contribution item to the start or end of the group
* with the given name.
*
* @param groupName the name of the group
* @param item the contribution item
* @param append <code>true</code> to add to the end of the group,
* and <code>false</code> to add the beginning of the group
* @exception IllegalArgumentException if there is no group with
* the given name
*/
private void addToGroup(String groupName, IContributionItem item,
boolean append) {
int i;
Iterator items = contributions.iterator();
for (i = 0; items.hasNext(); i++) {
IContributionItem o = (IContributionItem) items.next();
if (o.isGroupMarker()) {
String id = o.getId();
if (id != null && id.equalsIgnoreCase(groupName)) {
i++;
if (append) {
for (; items.hasNext(); i++) {
IContributionItem ci = (IContributionItem) items
.next();
if (ci.isGroupMarker())
break;
}
}
if (allowItem(item)) {
contributions.add(i, item);
itemAdded(item);
}
return;
}
}
}
throw new IllegalArgumentException("Group not found: " + groupName);//$NON-NLS-1$
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void appendToGroup(String groupName, IAction action) {
addToGroup(groupName, new ActionContributionItem(action), true);
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void appendToGroup(String groupName, IContributionItem item) {
addToGroup(groupName, item, true);
}
/**
* This method allows subclasses of <code>ContributionManager</code> to prevent
* certain items in the contributions list. <code>ContributionManager</code>
* will either block or allow an addition based on the result of this method
* call. This can be used to prevent duplication, for example.
* @param itemToAdd The contribution item to be added; may be <code>null</code>.
* @return <code>true</code> if the addition should be allowed;
* <code>false</code> otherwise. The default implementation allows all items.
* @since 3.0
*/
protected boolean allowItem(IContributionItem itemToAdd) {
return true;
}
/**
* Internal debug method for printing statistics about this manager
* to <code>System.out</code>.
*/
protected void dumpStatistics() {
int size = 0;
if (contributions != null)
size = contributions.size();
System.out.println(this.toString());
System.out.println(" Number of elements: " + size);//$NON-NLS-1$
int sum = 0;
for (int i = 0; i < size; i++)
if (((IContributionItem) contributions.get(i)).isVisible())
sum++;
System.out.println(" Number of visible elements: " + sum);//$NON-NLS-1$
System.out.println(" Is dirty: " + isDirty()); //$NON-NLS-1$
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public IContributionItem find(String id) {
Iterator e = contributions.iterator();
while (e.hasNext()) {
IContributionItem item = (IContributionItem) e.next();
String itemId = item.getId();
if (itemId != null && itemId.equalsIgnoreCase(id))
return item;
}
return null;
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public IContributionItem[] getItems() {
IContributionItem[] items = new IContributionItem[contributions.size()];
contributions.toArray(items);
return items;
}
/**
* The <code>ContributionManager</code> implementation of this
* method declared on <code>IContributionManager</code> returns
* the current overrides. If there is no overrides it lazily creates
* one which overrides no item state.
*
* @since 2.0
*/
public IContributionManagerOverrides getOverrides() {
if (overrides == null) {
overrides = new IContributionManagerOverrides() {
public Boolean getEnabled(IContributionItem item) {
return null;
}
public Integer getAccelerator(IContributionItem item) {
return null;
}
public String getAcceleratorText(IContributionItem item) {
return null;
}
public String getText(IContributionItem item) {
return null;
}
};
}
return overrides;
}
/**
* Returns whether this contribution manager contains dynamic items.
* A dynamic contribution item contributes items conditionally,
* dependent on some internal state.
*
* @return <code>true</code> if this manager contains dynamic items, and
* <code>false</code> otherwise
*/
protected boolean hasDynamicItems() {
return (dynamicItems > 0);
}
/**
* Returns the index of the item with the given id.
* @param id The id of the item whose index is requested.
*
* @return <code>int</code> the index or -1 if the item is not found
*/
public int indexOf(String id) {
for (int i = 0; i < contributions.size(); i++) {
IContributionItem item = (IContributionItem) contributions.get(i);
String itemId = item.getId();
if (itemId != null && itemId.equalsIgnoreCase(id))
return i;
}
return -1;
}
/**
* Returns the index of the object in the internal structure. This is different from
* <code>indexOf(String id)</code> since some contribution items may not have an id.
*
* @param item The contribution item
* @return the index, or -1 if the item is not found
* @since 3.0
*/
protected int indexOf(IContributionItem item) {
return contributions.indexOf(item);
}
/**
* Insert the item at the given index.
*
* @param index The index to be used for insertion
* @param item The item to be inserted
*/
public void insert(int index, IContributionItem item) {
if (index > contributions.size())
throw new IndexOutOfBoundsException(
"inserting " + item.getId() + " at " + index); //$NON-NLS-1$ //$NON-NLS-2$
if (allowItem(item)) {
contributions.add(index, item);
itemAdded(item);
}
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void insertAfter(String ID, IAction action) {
insertAfter(ID, new ActionContributionItem(action));
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void insertAfter(String ID, IContributionItem item) {
IContributionItem ci = find(ID);
if (ci == null)
throw new IllegalArgumentException("can't find ID");//$NON-NLS-1$
int ix = contributions.indexOf(ci);
if (ix >= 0) {
// System.out.println("insert after: " + ix);
if (allowItem(item)) {
contributions.add(ix + 1, item);
itemAdded(item);
}
}
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void insertBefore(String ID, IAction action) {
insertBefore(ID, new ActionContributionItem(action));
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void insertBefore(String ID, IContributionItem item) {
IContributionItem ci = find(ID);
if (ci == null)
throw new IllegalArgumentException("can't find ID " + ID);//$NON-NLS-1$
int ix = contributions.indexOf(ci);
if (ix >= 0) {
// System.out.println("insert before: " + ix);
if (allowItem(item)) {
contributions.add(ix, item);
itemAdded(item);
}
}
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public boolean isDirty() {
if (isDirty)
return true;
if (hasDynamicItems()) {
for (Iterator iter = contributions.iterator(); iter.hasNext();) {
IContributionItem item = (IContributionItem) iter.next();
if (item.isDirty())
return true;
}
}
return false;
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public boolean isEmpty() {
return contributions.isEmpty();
}
/**
* The given item was added to the list of contributions.
* Marks the manager as dirty and updates the number of dynamic items, and the memento.
*/
protected void itemAdded(IContributionItem item) {
item.setParent(this);
markDirty();
if (item.isDynamic())
dynamicItems++;
}
/**
* The given item was removed from the list of contributions.
* Marks the manager as dirty and updates the number of dynamic items.
*/
protected void itemRemoved(IContributionItem item) {
item.setParent(null);
markDirty();
if (item.isDynamic())
dynamicItems--;
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void markDirty() {
setDirty(true);
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void prependToGroup(String groupName, IAction action) {
addToGroup(groupName, new ActionContributionItem(action), false);
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void prependToGroup(String groupName, IContributionItem item) {
addToGroup(groupName, item, false);
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public IContributionItem remove(String ID) {
IContributionItem ci = find(ID);
if (ci == null)
return null;
return remove(ci);
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public IContributionItem remove(IContributionItem item) {
if (contributions.remove(item)) {
itemRemoved(item);
return item;
}
return null;
}
/* (non-Javadoc)
* Method declared on IContributionManager.
*/
public void removeAll() {
IContributionItem[] items = getItems();
contributions.clear();
for (int i = 0; i < items.length; i++) {
IContributionItem item = items[i];
itemRemoved(item);
}
dynamicItems = 0;
markDirty();
}
/**
* Replaces the item of the given identifier with another contribution item.
* This can be used, for example, to replace large contribution items with
* placeholders to avoid memory leaks. If the identifier cannot be found in the
* current list of items, then this does nothing. If multiple occurrences are
* found, then the replacement items is put in the first position and the other
* positions are removed.
* @param identifier The identifier to look for in the list of contributions;
* should not be <code>null</code>.
* @param replacementItem The contribution item to replace the old item; must
* not be <code>null</code>. Use {@link org.eclipse.jface.action.ContributionManager#remove(java.lang.String) remove} if that is what you want to do.
* @return <code>true</code> if the given identifier can be; <code>
* @since 3.0
*/
public boolean replaceItem(final String identifier,
final IContributionItem replacementItem) {
if (identifier == null) {
return false;
}
final int index = indexOf(identifier);
if (index < 0) {
return false; // couldn't find the item.
}
// Remove the old item.
final IContributionItem oldItem = (IContributionItem) contributions
.get(index);
itemRemoved(oldItem);
// Add the new item.
contributions.set(index, replacementItem);
itemAdded(replacementItem); // throws NPE if (replacementItem == null)
// Go through and remove duplicates.
for (int i = contributions.size() - 1; i > index; i--) {
IContributionItem item = (IContributionItem) contributions.get(i);
if ((item != null) && (identifier.equals(item.getId()))) {
if (Policy.TRACE_TOOLBAR) {
System.out
.println("Removing duplicate on replace: " + identifier); //$NON-NLS-1$
}
contributions.remove(i);
itemRemoved(item);
}
}
return true; // success
}
/**
* Sets whether this manager is dirty. When dirty, the list of contributions
* is not accurately reflected in the corresponding widgets.
*
* @param dirty <code>true</code> if this manager is dirty, and <code>false</code>
* if it is up-to-date
*/
protected void setDirty(boolean dirty) {
isDirty = dirty;
}
/**
* Sets the overrides for this contribution manager
*
* @param newOverrides the overrides for the items of this manager
* @since 2.0
*/
public void setOverrides(IContributionManagerOverrides newOverrides) {
overrides = newOverrides;
}
/**
* An internal method for setting the order of the contribution items.
*
* @param items the contribution items in the specified order
* @since 3.0
*/
protected void internalSetItems(IContributionItem[] items) {
contributions.clear();
for (int i = 0; i < items.length; i++) {
if (allowItem(items[i])) {
contributions.add(items[i]);
}
}
}
}