diff dwtx/jface/action/ToolBarContributionItem.d @ 25:ca63e2bea4bf

CoolBarManager
author Frank Benoit <benoit@tionex.de>
date Thu, 03 Apr 2008 04:50:25 +0200
parents
children ea8ff534f622
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dwtx/jface/action/ToolBarContributionItem.d	Thu Apr 03 04:50:25 2008 +0200
@@ -0,0 +1,691 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 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
+ * Port to the D programming language:
+ *     Frank Benoit <benoit@tionex.de>
+ *******************************************************************************/
+
+module dwtx.jface.action.ToolBarContributionItem;
+
+import dwtx.jface.action.IContributionItem;
+import dwtx.jface.action.ContributionItem;
+import dwtx.jface.action.MenuManager;
+import dwtx.jface.action.IToolBarManager;
+import dwtx.jface.action.ToolBarManager;
+import dwtx.jface.action.ActionContributionItem;
+import dwtx.jface.action.SubContributionItem;
+import dwtx.jface.action.ICoolBarManager;
+import dwtx.jface.action.Separator;
+import dwtx.jface.action.IContributionManager;
+
+import tango.util.collection.ArraySeq;
+// import java.util.Iterator;
+
+import dwt.DWT;
+import dwt.events.DisposeEvent;
+import dwt.events.DisposeListener;
+import dwt.events.SelectionAdapter;
+import dwt.events.SelectionEvent;
+import dwt.graphics.Point;
+import dwt.graphics.Rectangle;
+import dwt.widgets.Control;
+import dwt.widgets.CoolBar;
+import dwt.widgets.CoolItem;
+import dwt.widgets.Event;
+import dwt.widgets.Listener;
+import dwt.widgets.Menu;
+import dwt.widgets.ToolBar;
+import dwt.widgets.ToolItem;
+import dwtx.core.runtime.Assert;
+import dwtx.jface.internal.provisional.action.IToolBarContributionItem;
+import dwtx.jface.util.Policy;
+
+import dwt.dwthelper.utils;
+import tango.io.Stdout;
+
+/**
+ * The <code>ToolBarContributionItem</code> class provides a wrapper for tool
+ * bar managers when used in cool bar managers. It extends <code>ContributionItem</code>
+ * but and provides some additional methods to customize the size of the cool
+ * item and to retrieve the underlying tool bar manager.
+ * <p>
+ * This class may be instantiated; it is not intended to be subclassed.
+ * </p>
+ *
+ * @since 3.0
+ */
+public class ToolBarContributionItem : ContributionItem, IToolBarContributionItem {
+
+    public IContributionManager getParent() {
+        return super.getParent();
+    }
+
+    /**
+     * A constant used by <code>setMinimumItemsToShow</code> and <code>getMinimumItemsToShow</code>
+     * to indicate that all tool items should be shown in the cool item.
+     */
+    public static const int SHOW_ALL_ITEMS = -1;
+
+    /**
+     * The pull down menu used to list all hidden tool items if the current
+     * size is less than the preffered size.
+     */
+    private MenuManager chevronMenuManager = null;
+
+    /**
+     * The widget created for this item; <code>null</code> before creation
+     * and after disposal.
+     */
+    private CoolItem coolItem = null;
+
+    /**
+     * Current height of cool item
+     */
+    private int currentHeight = -1;
+
+    /**
+     * Current width of cool item.
+     */
+    private int currentWidth = -1;
+
+    /**
+     * A flag indicating that this item has been disposed. This prevents future
+     * method invocations from doing things they shouldn't.
+     */
+    private bool disposed = false;
+
+    /**
+     * Mininum number of tool items to show in the cool item widget.
+     */
+    private int minimumItemsToShow = SHOW_ALL_ITEMS;
+
+    /**
+     * The tool bar manager used to manage the tool items contained in the cool
+     * item widget.
+     */
+    private ToolBarManager toolBarManager = null;
+
+    /**
+     * Enable/disable chevron support.
+     */
+    private bool useChevron = true;
+
+    /**
+     * Convenience method equivalent to <code>ToolBarContributionItem(new ToolBarManager(), null)</code>.
+     */
+    public this() {
+        this(new ToolBarManager(), null);
+    }
+
+    /**
+     * Convenience method equivalent to <code>ToolBarContributionItem(toolBarManager, null)</code>.
+     *
+     * @param toolBarManager
+     *            the tool bar manager
+     */
+    public this(IToolBarManager toolBarManager) {
+        this(toolBarManager, null);
+    }
+
+    /**
+     * Creates a tool bar contribution item.
+     *
+     * @param toolBarManager
+     *            the tool bar manager to wrap
+     * @param id
+     *            the contribution item id, or <code>null</code> if none
+     */
+    public this(IToolBarManager toolBarManager, String id) {
+        super(id);
+        Assert.isTrue( null !is cast(ToolBarManager)toolBarManager );
+        this.toolBarManager = cast(ToolBarManager) toolBarManager;
+    }
+
+    /**
+     * Checks whether this contribution item has been disposed. If it has, and
+     * the tracing options are active, then it prints some debugging
+     * information.
+     *
+     * @return <code>true</code> if the item is disposed; <code>false</code>
+     *         otherwise.
+     *
+     */
+    private final bool checkDisposed() {
+        if (disposed) {
+            if (Policy.TRACE_TOOLBAR) {
+                Stdout.formatln("Method invocation on a disposed tool bar contribution item."); //$NON-NLS-1$
+                ExceptionPrintStackTrace( new Exception(null), Stdout );
+            }
+
+            return true;
+        }
+
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.action.IContributionItem#dispose()
+     */
+    public void dispose() {
+        // Dispose of the ToolBar and all its contributions
+        if (toolBarManager !is null) {
+            toolBarManager.dispose();
+            toolBarManager = null;
+        }
+
+        /*
+         * We need to dispose the cool item or we might be left holding a cool
+         * item with a disposed control.
+         */
+        if ((coolItem !is null) && (!coolItem.isDisposed())) {
+            coolItem.dispose();
+            coolItem = null;
+        }
+
+        // Mark this item as disposed.
+        disposed = true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.action.IContributionItem#fill(dwt.widgets.CoolBar,
+     *      int)
+     */
+    public void fill(CoolBar coolBar, int index) {
+        if (checkDisposed()) {
+            return;
+        }
+
+        if (coolItem is null && coolBar !is null) {
+            ToolBar oldToolBar = toolBarManager.getControl();
+            ToolBar toolBar = toolBarManager.createControl(coolBar);
+            if ((oldToolBar !is null) && (oldToolBar.opEquals(toolBar))) {
+                // We are using an old tool bar, so we need to update.
+                toolBarManager.update(true);
+            }
+
+            // Do not create a coolItem if the toolbar is empty
+            if (toolBar.getItemCount() < 1) {
+                return;
+            }
+            int flags = DWT.DROP_DOWN;
+            if (index >= 0) {
+                coolItem = new CoolItem(coolBar, flags, index);
+            } else {
+                coolItem = new CoolItem(coolBar, flags);
+            }
+            // sets the back reference
+            coolItem.setData(this);
+            // Add the toolbar to the CoolItem widget
+            coolItem.setControl(toolBar);
+
+            // Handle Context Menu
+            // ToolBarManager.createControl can actually return a pre-existing control.
+            // Only add the listener if the toolbar was newly created (bug 62097).
+            if (oldToolBar !is toolBar) {
+                toolBar.addListener(DWT.MenuDetect, new class Listener {
+
+                    public void handleEvent(Event event) {
+                        // if the toolbar does not have its own context menu then
+                        // handle the event
+                        if (toolBarManager.getContextMenuManager() is null) {
+                            handleContextMenu(event);
+                        }
+                    }
+                });
+            }
+
+            // Handle for chevron clicking
+            if (getUseChevron()) {
+                // Chevron Support
+                coolItem.addSelectionListener(new class SelectionAdapter {
+
+                    public void widgetSelected(SelectionEvent event) {
+                        if (event.detail is DWT.ARROW) {
+                            handleChevron(event);
+                        }
+                    }
+                });
+            }
+
+            // Handle for disposal
+            coolItem.addDisposeListener(new class DisposeListener {
+
+                public void widgetDisposed(DisposeEvent event) {
+                    handleWidgetDispose(event);
+                }
+            });
+
+            // Sets the size of the coolItem
+            updateSize(true);
+        }
+    }
+
+    /**
+     * Returns a consistent set of wrap indices. The return value will always
+     * include at least one entry and the first entry will always be zero.
+     * CoolBar.getWrapIndices() is inconsistent in whether or not it returns an
+     * index for the first row.
+     */
+    private int[] getAdjustedWrapIndices(int[] wraps) {
+        int[] adjustedWrapIndices;
+        if (wraps.length is 0) {
+            adjustedWrapIndices = [ 0 ];
+        } else {
+            if (wraps[0] !is 0) {
+                adjustedWrapIndices = new int[wraps.length + 1];
+                adjustedWrapIndices[0] = 0;
+                for (int i = 0; i < wraps.length; i++) {
+                    adjustedWrapIndices[i + 1] = wraps[i];
+                }
+            } else {
+                adjustedWrapIndices = wraps;
+            }
+        }
+        return adjustedWrapIndices;
+    }
+
+    /**
+     * Returns the current height of the corresponding cool item.
+     *
+     * @return the current height
+     */
+    public int getCurrentHeight() {
+        if (checkDisposed()) {
+            return -1;
+        }
+        return currentHeight;
+    }
+
+    /**
+     * Returns the current width of the corresponding cool item.
+     *
+     * @return the current size
+     */
+    public int getCurrentWidth() {
+        if (checkDisposed()) {
+            return -1;
+        }
+        return currentWidth;
+    }
+
+    /**
+     * Returns the minimum number of tool items to show in the cool item.
+     *
+     * @return the minimum number of tool items to show, or <code>SHOW_ALL_ITEMS</code>
+     *         if a value was not set
+     * @see #setMinimumItemsToShow(int)
+     */
+    public int getMinimumItemsToShow() {
+        if (checkDisposed()) {
+            return -1;
+        }
+        return minimumItemsToShow;
+    }
+
+    /**
+     * Returns the internal tool bar manager of the contribution item.
+     *
+     * @return the tool bar manager, or <code>null</code> if one is not
+     *         defined.
+     * @see IToolBarManager
+     */
+    public IToolBarManager getToolBarManager() {
+        if (checkDisposed()) {
+            return null;
+        }
+        return toolBarManager;
+    }
+
+    /**
+     * Returns whether chevron support is enabled.
+     *
+     * @return <code>true</code> if chevron support is enabled, <code>false</code>
+     *         otherwise
+     */
+    public bool getUseChevron() {
+        if (checkDisposed()) {
+            return false;
+        }
+        return useChevron;
+    }
+
+    /**
+     * Create and display the chevron menu.
+     */
+    private void handleChevron(SelectionEvent event) {
+        CoolItem item = cast(CoolItem) event.widget;
+        Control control = item.getControl();
+        if (!(cast(ToolBar)control ) ) {
+            return;
+        }
+        CoolBar coolBar = item.getParent();
+        ToolBar toolBar = cast(ToolBar) control;
+        Rectangle toolBarBounds = toolBar.getBounds();
+        ToolItem[] items = toolBar.getItems();
+        auto hidden = new ArraySeq!(Object);
+        for (int i = 0; i < items.length; ++i) {
+            Rectangle itemBounds = items[i].getBounds();
+            if (!((itemBounds.x + itemBounds.width <= toolBarBounds.width) && (itemBounds.y
+                    + itemBounds.height <= toolBarBounds.height))) {
+                hidden.append(items[i]);
+            }
+        }
+
+        // Create a pop-up menu with items for each of the hidden buttons.
+        if (chevronMenuManager !is null) {
+            chevronMenuManager.dispose();
+        }
+        chevronMenuManager = new MenuManager();
+        foreach( it; hidden ){
+            ToolItem toolItem = cast(ToolItem) it;
+            IContributionItem data = cast(IContributionItem) toolItem.getData();
+            if (cast(ActionContributionItem)data ) {
+                ActionContributionItem contribution = new ActionContributionItem(
+                        (cast(ActionContributionItem) data).getAction());
+                chevronMenuManager.add(contribution);
+            } else if (cast(SubContributionItem)data ) {
+                IContributionItem innerData = (cast(SubContributionItem) data)
+                        .getInnerItem();
+                if (cast(ActionContributionItem)innerData ) {
+                    ActionContributionItem contribution = new ActionContributionItem(
+                            (cast(ActionContributionItem) innerData).getAction());
+                    chevronMenuManager.add(contribution);
+                }
+            } else if (data.isSeparator()) {
+                chevronMenuManager.add(new Separator());
+            }
+        }
+        Menu popup = chevronMenuManager.createContextMenu(coolBar);
+        Point chevronPosition = coolBar.toDisplay(event.x, event.y);
+        popup.setLocation(chevronPosition.x, chevronPosition.y);
+        popup.setVisible(true);
+    }
+
+    /**
+     * Handles the event when the toobar item does not have its own context
+     * menu.
+     *
+     * @param event
+     *            the event object
+     */
+    private void handleContextMenu(Event event) {
+        ToolBar toolBar = toolBarManager.getControl();
+        // If parent has a menu then use that one
+        Menu parentMenu = toolBar.getParent().getMenu();
+        if ((parentMenu !is null) && (!parentMenu.isDisposed())) {
+            toolBar.setMenu(parentMenu);
+            // Hook listener to remove menu once it has disapeared
+            parentMenu.addListener(DWT.Hide, new class Listener {
+
+                public void handleEvent(Event innerEvent) {
+                    ToolBar innerToolBar = toolBarManager.getControl();
+                    if (innerToolBar !is null) {
+                        innerToolBar.setMenu(null);
+                        Menu innerParentMenu = innerToolBar.getParent()
+                                .getMenu();
+                        if (innerParentMenu !is null) {
+                            innerParentMenu.removeListener(DWT.Hide, this);
+                        }
+                    }
+                }
+            });
+        }
+    }
+
+    /**
+     * Handles the disposal of the widget.
+     *
+     * @param event
+     *            the event object
+     */
+    private void handleWidgetDispose(DisposeEvent event) {
+        coolItem = null;
+    }
+
+    /**
+     * A contribution item is visible iff its internal state is visible <em>or</em>
+     * the tool bar manager contains something other than group markers and
+     * separators.
+     *
+     * @return <code>true</code> if the tool bar manager contains something
+     *         other than group marks and separators, and the internal state is
+     *         set to be visible.
+     */
+    public bool isVisible() {
+        if (checkDisposed()) {
+            return false;
+        }
+
+        bool visibleItem = false;
+        if (toolBarManager !is null) {
+            IContributionItem[] contributionItems = toolBarManager.getItems();
+            for (int i = 0; i < contributionItems.length; i++) {
+                IContributionItem contributionItem = contributionItems[i];
+                if ((!contributionItem.isGroupMarker())
+                        && (!contributionItem.isSeparator())) {
+                    visibleItem = true;
+                    break;
+                }
+            }
+        }
+
+        return (visibleItem || super.isVisible());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.action.IContributionItem#saveWidgetState()
+     */
+    public void saveWidgetState() {
+        if (checkDisposed()) {
+            return;
+        }
+        if (coolItem is null) {
+            return;
+        }
+
+        //1. Save current size
+        CoolBar coolBar = coolItem.getParent();
+        bool isLastOnRow = false;
+        int lastIndex = coolBar.getItemCount() - 1;
+        int coolItemIndex = coolBar.indexOf(coolItem);
+        int[] wrapIndicies = getAdjustedWrapIndices(coolBar.getWrapIndices());
+        // Traverse through all wrap indicies backwards
+        for (int row = wrapIndicies.length - 1; row >= 0; row--) {
+            if (wrapIndicies[row] <= coolItemIndex) {
+
+                int nextRow = row + 1;
+                int nextRowStartIndex;
+                if (nextRow > (wrapIndicies.length - 1)) {
+                    nextRowStartIndex = lastIndex + 1;
+                } else {
+                    nextRowStartIndex = wrapIndicies[nextRow];
+                }
+
+                // Check to see if its the last item on the row
+                if (coolItemIndex is (nextRowStartIndex - 1)) {
+                    isLastOnRow = true;
+                }
+                break;
+            }
+        }
+
+        // Save the preferred size as actual size for the last item on a row
+        int nCurrentWidth;
+        if (isLastOnRow) {
+            nCurrentWidth = coolItem.getPreferredSize().x;
+        } else {
+            nCurrentWidth = coolItem.getSize().x;
+        }
+        setCurrentWidth(nCurrentWidth);
+        setCurrentHeight(coolItem.getSize().y);
+    }
+
+    /**
+     * Sets the current height of the cool item. Update(SIZE) should be called
+     * to adjust the widget.
+     *
+     * @param currentHeight
+     *            the current height to set
+     */
+    public void setCurrentHeight(int currentHeight) {
+        if (checkDisposed()) {
+            return;
+        }
+        this.currentHeight = currentHeight;
+    }
+
+    /**
+     * Sets the current width of the cool item. Update(SIZE) should be called
+     * to adjust the widget.
+     *
+     * @param currentWidth
+     *            the current width to set
+     */
+    public void setCurrentWidth(int currentWidth) {
+        if (checkDisposed()) {
+            return;
+        }
+        this.currentWidth = currentWidth;
+    }
+
+    /**
+     * Sets the minimum number of tool items to show in the cool item. If this
+     * number is less than the total tool items, a chevron will appear and the
+     * hidden tool items appear in a drop down menu. By default, all the tool
+     * items are shown in the cool item.
+     *
+     * @param minimumItemsToShow
+     *            the minimum number of tool items to show.
+     * @see #getMinimumItemsToShow()
+     * @see #setUseChevron(bool)
+     */
+    public void setMinimumItemsToShow(int minimumItemsToShow) {
+        if (checkDisposed()) {
+            return;
+        }
+        this.minimumItemsToShow = minimumItemsToShow;
+    }
+
+    /**
+     * Enables or disables chevron support for the cool item. By default,
+     * chevron support is enabled.
+     *
+     * @param value
+     *            <code>true</code> to enable chevron support, <code>false</code>
+     *            otherwise.
+     */
+    public void setUseChevron(bool value) {
+        if (checkDisposed()) {
+            return;
+        }
+        useChevron = value;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see dwtx.jface.action.IContributionItem#update(java.lang.String)
+     */
+    public void update(String propertyName) {
+        if (checkDisposed()) {
+            return;
+        }
+        if (coolItem !is null) {
+            IToolBarManager manager = getToolBarManager();
+            if (manager !is null) {
+                manager.update(true);
+            }
+
+            if ((propertyName is null)
+                    || propertyName.equals(ICoolBarManager.SIZE)) {
+                updateSize(true);
+            }
+        }
+    }
+
+    /**
+     * Updates the cool items' preferred, minimum, and current size. The
+     * preferred size is calculated based on the tool bar size and extra trim.
+     *
+     * @param changeCurrentSize
+     *            <code>true</code> if the current size should be changed to
+     *            the preferred size, <code>false</code> to not change the
+     *            current size
+     */
+    private void updateSize(bool changeCurrentSize) {
+        if (checkDisposed()) {
+            return;
+        }
+        // cannot set size if coolItem is null
+        if (coolItem is null || coolItem.isDisposed()) {
+            return;
+        }
+        bool locked = false;
+        CoolBar coolBar = coolItem.getParent();
+        try {
+            // Fix odd behaviour with locked tool bars
+            if (coolBar !is null) {
+                if (coolBar.getLocked()) {
+                    coolBar.setLocked(false);
+                    locked = true;
+                }
+            }
+            ToolBar toolBar = cast(ToolBar) coolItem.getControl();
+            if ((toolBar is null) || (toolBar.isDisposed())
+                    || (toolBar.getItemCount() <= 0)) {
+                // if the toolbar does not contain any items then dispose of
+                // coolItem
+                coolItem.setData(null);
+                Control control = coolItem.getControl();
+                if ((control !is null) && !control.isDisposed()) {
+                    control.dispose();
+                    coolItem.setControl(null);
+                }
+                if (!coolItem.isDisposed()) {
+                    coolItem.dispose();
+                }
+            } else {
+                // If the toolbar item exists then adjust the size of the cool
+                // item
+                Point toolBarSize = toolBar.computeSize(DWT.DEFAULT,
+                        DWT.DEFAULT);
+                // Set the preffered size to the size of the toolbar plus trim
+                Point preferredSize = coolItem.computeSize(toolBarSize.x,
+                        toolBarSize.y);
+                coolItem.setPreferredSize(preferredSize);
+                // note setMinimumSize must be called before setSize, see PR
+                // 15565
+                // Set minimum size
+                if (getMinimumItemsToShow() !is SHOW_ALL_ITEMS) {
+                    int toolItemWidth = toolBar.getItems()[0].getWidth();
+                    int minimumWidth = toolItemWidth * getMinimumItemsToShow();
+                    coolItem.setMinimumSize(minimumWidth, toolBarSize.y);
+                } else {
+                    coolItem.setMinimumSize(toolBarSize.x, toolBarSize.y);
+                }
+                if (changeCurrentSize) {
+                    // Set current size to preferred size
+                    coolItem.setSize(preferredSize);
+                }
+            }
+        } finally {
+            // If the cool bar was locked, then set it back to locked
+            if ((locked) && (coolBar !is null)) {
+                coolBar.setLocked(true);
+            }
+        }
+    }
+
+}