| /******************************************************************************* |
| * 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 org.eclipse.jface.util.Assert; |
| import org.eclipse.jface.util.Policy; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.graphics.Rectangle; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.CoolBar; |
| import org.eclipse.swt.widgets.CoolItem; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Menu; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.ToolItem; |
| |
| /** |
| * 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 extends ContributionItem { |
| |
| /** |
| * 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 final 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 boolean 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 boolean useChevron = true; |
| |
| /** |
| * Convenience method equivalent to <code>ToolBarContributionItem(new ToolBarManager(), null)</code>. |
| */ |
| public ToolBarContributionItem() { |
| this(new ToolBarManager(), null); |
| } |
| |
| /** |
| * Convenience method equivalent to <code>ToolBarContributionItem(toolBarManager, null)</code>. |
| * |
| * @param toolBarManager |
| * the tool bar manager |
| */ |
| public ToolBarContributionItem(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 ToolBarContributionItem(IToolBarManager toolBarManager, String id) { |
| super(id); |
| Assert.isTrue(toolBarManager instanceof ToolBarManager); |
| this.toolBarManager = (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 boolean checkDisposed() { |
| if (disposed) { |
| if (Policy.TRACE_TOOLBAR) { |
| System.out |
| .println("Method invocation on a disposed tool bar contribution item."); //$NON-NLS-1$ |
| new Exception().printStackTrace(System.out); |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.action.IContributionItem#dispose() |
| */ |
| public void dispose() { |
| // Dispose of the ToolBar and all its contributions |
| if (toolBarManager != 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 != null) && (!coolItem.isDisposed())) { |
| coolItem.dispose(); |
| coolItem = null; |
| } |
| |
| // Mark this item as disposed. |
| disposed = true; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.action.IContributionItem#fill(org.eclipse.swt.widgets.CoolBar, |
| * int) |
| */ |
| public void fill(CoolBar coolBar, int index) { |
| if (checkDisposed()) { |
| return; |
| } |
| |
| if (coolItem == null && coolBar != null) { |
| ToolBar oldToolBar = toolBarManager.getControl(); |
| ToolBar toolBar = toolBarManager.createControl(coolBar); |
| if ((oldToolBar != null) && (oldToolBar.equals(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 = SWT.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 != toolBar) { |
| toolBar.addListener(SWT.MenuDetect, new Listener() { |
| |
| public void handleEvent(Event event) { |
| // if the toolbar does not have its own context menu then |
| // handle the event |
| if (toolBarManager.getContextMenuManager() == null) { |
| handleContextMenu(event); |
| } |
| } |
| }); |
| } |
| |
| // Handle for chevron clicking |
| if (getUseChevron()) { |
| // Chevron Support |
| coolItem.addSelectionListener(new SelectionAdapter() { |
| |
| public void widgetSelected(SelectionEvent event) { |
| if (event.detail == SWT.ARROW) { |
| handleChevron(event); |
| } |
| } |
| }); |
| } |
| |
| // Handle for disposal |
| coolItem.addDisposeListener(new 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 == 0) { |
| adjustedWrapIndices = new int[] { 0 }; |
| } else { |
| if (wraps[0] != 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 boolean getUseChevron() { |
| if (checkDisposed()) { |
| return false; |
| } |
| return useChevron; |
| } |
| |
| /** |
| * Create and display the chevron menu. |
| */ |
| private void handleChevron(SelectionEvent event) { |
| CoolItem item = (CoolItem) event.widget; |
| Control control = item.getControl(); |
| if ((control instanceof ToolBar) == false) { |
| return; |
| } |
| CoolBar coolBar = item.getParent(); |
| ToolBar toolBar = (ToolBar) control; |
| Rectangle toolBarBounds = toolBar.getBounds(); |
| ToolItem[] items = toolBar.getItems(); |
| ArrayList hidden = new ArrayList(); |
| 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.add(items[i]); |
| } |
| } |
| |
| // Create a pop-up menu with items for each of the hidden buttons. |
| if (chevronMenuManager != null) { |
| chevronMenuManager.dispose(); |
| } |
| chevronMenuManager = new MenuManager(); |
| for (Iterator i = hidden.iterator(); i.hasNext();) { |
| ToolItem toolItem = (ToolItem) i.next(); |
| IContributionItem data = (IContributionItem) toolItem.getData(); |
| if (data instanceof ActionContributionItem) { |
| ActionContributionItem contribution = new ActionContributionItem( |
| ((ActionContributionItem) data).getAction()); |
| chevronMenuManager.add(contribution); |
| } else if (data instanceof SubContributionItem) { |
| IContributionItem innerData = ((SubContributionItem) data) |
| .getInnerItem(); |
| if (innerData instanceof ActionContributionItem) { |
| ActionContributionItem contribution = new ActionContributionItem( |
| ((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 != null) && (!parentMenu.isDisposed())) { |
| toolBar.setMenu(parentMenu); |
| // Hook listener to remove menu once it has disapeared |
| parentMenu.addListener(SWT.Hide, new Listener() { |
| |
| public void handleEvent(Event innerEvent) { |
| ToolBar innerToolBar = toolBarManager.getControl(); |
| if (innerToolBar != null) { |
| innerToolBar.setMenu(null); |
| Menu innerParentMenu = innerToolBar.getParent() |
| .getMenu(); |
| if (innerParentMenu != null) { |
| innerParentMenu.removeListener(SWT.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 boolean isVisible() { |
| if (checkDisposed()) { |
| return false; |
| } |
| |
| boolean visibleItem = false; |
| if (toolBarManager != 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 org.eclipse.jface.action.IContributionItem#saveWidgetState() |
| */ |
| public void saveWidgetState() { |
| if (checkDisposed()) { |
| return; |
| } |
| if (coolItem == null) |
| return; |
| |
| //1. Save current size |
| CoolBar coolBar = coolItem.getParent(); |
| boolean 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 == (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(boolean) |
| */ |
| 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(boolean value) { |
| if (checkDisposed()) { |
| return; |
| } |
| useChevron = value; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jface.action.IContributionItem#update(java.lang.String) |
| */ |
| public void update(String propertyName) { |
| if (checkDisposed()) { |
| return; |
| } |
| if (coolItem != null) { |
| IToolBarManager manager = getToolBarManager(); |
| if (manager != null) { |
| manager.update(true); |
| } |
| |
| if ((propertyName == 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(boolean changeCurrentSize) { |
| if (checkDisposed()) { |
| return; |
| } |
| // cannot set size if coolItem is null |
| if (coolItem == null || coolItem.isDisposed()) { |
| return; |
| } |
| boolean locked = false; |
| CoolBar coolBar = coolItem.getParent(); |
| try { |
| // Fix odd behaviour with locked tool bars |
| if (coolBar != null) { |
| if (coolBar.getLocked()) { |
| coolBar.setLocked(false); |
| locked = true; |
| } |
| } |
| ToolBar toolBar = (ToolBar) coolItem.getControl(); |
| if ((toolBar == 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 != 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(SWT.DEFAULT, |
| SWT.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() != 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 != null)) { |
| coolBar.setLocked(true); |
| } |
| } |
| } |
| |
| } |