diff dwtx/jface/action/ContributionManager.d @ 16:e0f0aaf75edd

PopupDialog, bindings and actions
author Frank Benoit <benoit@tionex.de>
date Tue, 01 Apr 2008 08:00:31 +0200
parents
children da5ad8eedf5d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/ContributionManager.d	Tue Apr 01 08:00:31 2008 +0200
@@ -0,0 +1,594 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+module dwtx.jface.action.ContributionManager;
+
+import dwtx.jface.action.ActionContributionItem;
+
+import dwtx.jface.action.IContributionManager;
+import dwtx.jface.action.IContributionItem;
+import dwtx.jface.action.IContributionManagerOverrides;
+import dwtx.jface.action.IAction;
+
+import tango.util.collection.ArraySeq;
+import tango.util.collection.model.Seq;
+
+import dwtx.jface.util.Policy;
+
+import dwt.dwthelper.utils;
+import dwt.dwthelper.Integer;
+import tango.io.Stdout;
+import tango.core.Exception;
+import tango.text.convert.Format;
+
+/**
+ * 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 : IContributionManager {
+
+    // Internal debug flag.
+    // protected static final bool DEBUG = false;
+
+    /**
+     * The list of contribution items.
+     */
+    private Seq!(IContributionItem) contributions;
+
+    /**
+     * Indicates whether the widgets are in sync with the contributions.
+     */
+    private bool 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 this() {
+        contributions = new ArraySeq!(IContributionItem);
+        // 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.append(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,
+            bool append) {
+        int i;
+        auto items = contributions.elements();
+        for (i = 0; items.more(); i++) {
+            IContributionItem o = cast(IContributionItem) items.get();
+            if (o.isGroupMarker()) {
+                String id = o.getId();
+                if (id !is null && id.equalsIgnoreCase(groupName)) {
+                    i++;
+                    if (append) {
+                        for (; items.more(); i++) {
+                            IContributionItem ci = cast(IContributionItem) items
+                                    .get();
+                            if (ci.isGroupMarker()) {
+                                break;
+                            }
+                        }
+                    }
+                    if (allowItem(item)) {
+                        //TODO: does this corrupt the iterator?
+                        contributions.addAt(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 bool 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 !is null) {
+            size = contributions.size();
+        }
+
+        Stdout.formatln(this.toString());
+        Stdout.formatln("   Number of elements: {}", size);//$NON-NLS-1$
+        int sum = 0;
+        for (int i = 0; i < size; i++) {
+            if ((cast(IContributionItem) contributions.get(i)).isVisible()) {
+                sum++;
+            }
+        }
+        Stdout.formatln("   Number of visible elements: {}", sum);//$NON-NLS-1$
+        Stdout.formatln("   Is dirty: {}", isDirty()); //$NON-NLS-1$
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public IContributionItem find(String id) {
+        auto e = contributions.elements();
+        while (e.more()) {
+            IContributionItem item = cast(IContributionItem) e.get();
+            String itemId = item.getId();
+            if (itemId !is null && itemId.equalsIgnoreCase(id)) {
+                return item;
+            }
+        }
+        return null;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public IContributionItem[] getItems() {
+        return contributions.toArray();
+    }
+
+    /**
+     * Return the number of contributions in this manager.
+     *
+     * @return the number of contributions in this manager
+     * @since 3.3
+     */
+    public int getSize() {
+        return contributions.size();
+    }
+
+    /**
+     * 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 is null) {
+            overrides = new class IContributionManagerOverrides {
+                public ValueWrapperBool getEnabled(IContributionItem item) {
+                    return null;
+                }
+
+                public ValueWrapperInt 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 bool 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 = cast(IContributionItem) contributions.get(i);
+            String itemId = item.getId();
+            if (itemId !is 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) {
+        int res = -1;
+        int idx = 0;
+        foreach( e; contributions ){
+            if( e == item ) {
+                res = idx;
+                break;
+            }
+            idx++;
+        }
+        return res;
+    }
+
+    /**
+     * 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( Format(
+                    "inserting {} at {}", item.getId(), index)); //$NON-NLS-1$ //$NON-NLS-2$
+        }
+        if (allowItem(item)) {
+            contributions.addAt(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 is null) {
+            throw new IllegalArgumentException(Format("can't find ID{}", ID));//$NON-NLS-1$
+        }
+        int ix = SeqIndexOf!(IContributionItem)( contributions, ci );
+        if (ix >= 0) {
+            // System.out.println("insert after: " + ix);
+            if (allowItem(item)) {
+                contributions.addAt(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 is null) {
+            throw new IllegalArgumentException(Format("can't find ID {}", ID));//$NON-NLS-1$
+        }
+        int ix = SeqIndexOf!(IContributionItem)(contributions,ci);
+        if (ix >= 0) {
+            // System.out.println("insert before: " + ix);
+            if (allowItem(item)) {
+                contributions.addAt(ix, item);
+                itemAdded(item);
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public bool isDirty() {
+        if (isDirty_) {
+            return true;
+        }
+        if (hasDynamicItems()) {
+            foreach( e; contributions ){
+                IContributionItem item = cast(IContributionItem) e;
+                if (item.isDirty()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public bool isEmpty() {
+        return contributions.drained();
+    }
+
+    /**
+     * 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.
+     *
+     * @param item
+     *            the item to be added
+     *
+     */
+    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.
+     *
+     * @param item
+     *            remove given parent from list of contributions
+     */
+    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 is null) {
+            return null;
+        }
+        return remove(ci);
+    }
+
+    /*
+     * (non-Javadoc) Method declared on IContributionManager.
+     */
+    public IContributionItem remove(IContributionItem item) {
+        bool contained = contributions.contains(item);
+        contributions.remove(item);
+        if (contained) {
+            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 dwtx.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 bool replaceItem(String identifier,
+            IContributionItem replacementItem) {
+        if (identifier is null) {
+            return false;
+        }
+
+        int index = indexOf(identifier);
+        if (index < 0) {
+            return false; // couldn't find the item.
+        }
+
+        // Remove the old item.
+        IContributionItem oldItem = cast(IContributionItem) contributions
+                .get(index);
+        itemRemoved(oldItem);
+
+        // Add the new item.
+        contributions.replaceAt(index, replacementItem);
+        itemAdded(replacementItem); // throws NPE if (replacementItem is null)
+
+        // Go through and remove duplicates.
+        for (int i = contributions.size() - 1; i > index; i--) {
+            IContributionItem item = cast(IContributionItem) contributions.get(i);
+            if ((item !is null) && (identifier.equals(item.getId()))) {
+                if (Policy.TRACE_TOOLBAR) {
+                    Stdout.formatln("Removing duplicate on replace: {}", identifier); //$NON-NLS-1$
+                }
+                contributions.removeAt(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(bool 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.append(items[i]);
+            }
+        }
+    }
+}