blob: 46cd8956d53f4d1c39942cb8b43d83a5dd9f3b3e [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 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);
}
}
}
}