blob: 81d5d88fb503d7780e7d7ad337656685bc66105f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 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
*******************************************************************************/
package org.eclipse.ui.internal.layout;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
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.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.internal.IChangeListener;
import org.eclipse.ui.internal.IntModel;
import org.eclipse.ui.internal.RadioMenu;
import org.eclipse.ui.internal.WindowTrimProxy;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.dnd.DragUtil;
import org.eclipse.ui.presentations.PresentationUtil;
/**
* This control provides common UI functionality for trim elements. Its
* lifecycle is managed by the <code>TrimLayout</code> which automatically
* adds a UI handle to all added trim elements. It uses an instance of a
* CoolBar to provide the platform-specific drag affordance.
* <p>
* It provides the following features:
* <p>
* Drag affordance and handling:
* <ol>
* <li>Drag affordance is provided in the <code>paintControl</code> method</li>
* <li>Drag handling is provided to allow rearrangement within a trim side or
* to other sides, depending on the values returned by <code>IWindowTrim.getValidSides</code></li>
* </ol>
* </p>
* <p>
* Context Menu:
* <ol>
* <li>A "Dock on" menu item is provided to allow changing the side, depending on the values returned by
* <code>IWindowTrim.getValidSides</code></li>
* <li>A "Close" menu item is provided to allow the User to close (hide) the trim element,
* based on the value returned by <code>IWindowTrim.isCloseable</code>
* </ol>
* </p>
* <p>
* @since 3.2
* </p>
*/
public class TrimCommonUIHandle extends Composite {
/*
* Fields
*/
private TrimLayout layout;
private IWindowTrim trim;
private Control toDrag;
private int orientation;
// CoolBar handling
private CoolBar cb = null;
private CoolItem ci = null;
private static int horizontalHandleSize = -1;
private static int verticalHandleSize = -1;
/*
* Context Menu
*/
private MenuManager dockMenuManager;
private ContributionItem dockContributionItem = null;
private Menu sidesMenu;
private MenuItem dockCascade;
private RadioMenu radioButtons;
private IntModel radioVal = new IntModel(0);
// private Menu showMenu;
// private MenuItem showCascade;
/*
* Listeners...
*/
/**
* This listener starts a drag operation when
* the Drag and Drop manager tells it to
*/
private Listener dragListener = new Listener() {
public void handleEvent(Event event) {
// Only allow 'left mouse' drags...
if (event.button != 3) {
Point position = DragUtil.getEventLoc(event);
startDraggingTrim(position);
}
}
};
/**
* This listener brings up the context menu
*/
private Listener menuListener = new Listener() {
public void handleEvent(Event event) {
Point loc = new Point(event.x, event.y);
if (event.type == SWT.MenuDetect) {
showDockTrimPopup(loc);
}
}
};
/**
* Listen to size changes in the control so we can adjust the
* Coolbar and CoolItem to match.
*/
private ControlListener controlListener = new ControlListener() {
public void controlMoved(ControlEvent e) {
}
public void controlResized(ControlEvent e) {
if (e.widget instanceof TrimCommonUIHandle) {
TrimCommonUIHandle ctrl = (TrimCommonUIHandle) e.widget;
Point size = ctrl.getSize();
// Set the CoolBar and item to match the handle's size
cb.setSize(size);
ci.setSize(size);
ci.setPreferredSize(size);
cb.layout(true);
}
}
};
/**
* Create a new trim UI handle for a particular IWindowTrim item
*
* @param layout the TrimLayout we're being used in
* @param trim the IWindowTrim we're acting on behalf of
* @param curSide the SWT side that the trim is currently on
*/
public TrimCommonUIHandle(TrimLayout layout, IWindowTrim trim, int curSide) {
super(trim.getControl().getParent(), SWT.NONE);
// Set the control up with all its various hooks, cursor...
setup(layout, trim, curSide);
// Listen to size changes to keep the CoolBar synched
addControlListener(controlListener);
}
/**
* Set up the trim with its cursor, drag listener, context menu and menu listener.
* This method can also be used to 'recycle' a trim handle as long as the new handle
* is for trim under the same parent as it was originally used for.
*/
public void setup(TrimLayout layout, IWindowTrim trim, int curSide) {
this.layout = layout;
this.trim = trim;
this.toDrag = trim.getControl();
this.radioVal.set(curSide);
// remember the orientation to use
orientation = (curSide == SWT.LEFT || curSide == SWT.RIGHT) ? SWT.VERTICAL : SWT.HORIZONTAL;
// Insert a CoolBar and extras in order to provide the drag affordance
insertCoolBar(orientation);
// Create a window trim proxy for the handle
createWindowTrimProxy();
// Set the cursor affordance
setDragCursor();
// Set up the dragging behaviour
PresentationUtil.addDragListener(cb, dragListener);
// Create the docking context menu
dockMenuManager = new MenuManager();
dockContributionItem = getDockingContribution();
dockMenuManager.add(dockContributionItem);
cb.addListener(SWT.MenuDetect, menuListener);
setVisible(true);
}
/**
* Handle the event generated when a User selects a new side to
* dock this trim on using the context menu
*/
private void handleShowOnChange() {
layout.removeTrim(trim);
trim.dock(radioVal.get());
layout.addTrim(radioVal.get(), trim, null);
// perform an optimized layout to show the trim in its new location
LayoutUtil.resize(trim.getControl());
}
/**
* Create and format the IWindowTrim for the handle, ensuring that the
* handle will be 'wide' enough to display the drag affordance.
*/
private void createWindowTrimProxy() {
// Create a window trim proxy for the handle
WindowTrimProxy proxy = new WindowTrimProxy(this, "NONE", "NONE", //$NON-NLS-1$ //$NON-NLS-2$
SWT.TOP | SWT.BOTTOM | SWT.LEFT | SWT.RIGHT, false);
// Set up the handle's hints based on the computed size that
// the handle has to be (i.e. if it's HORIZONTAL then the
// 'width' is determined by the space required to show the
// CB's drag affordance).
if (orientation == SWT.HORIZONTAL) {
proxy.setWidthHint(getHandleSize());
proxy.setHeightHint(0);
}
else {
proxy.setWidthHint(0);
proxy.setHeightHint(getHandleSize());
}
setLayoutData(proxy);
}
/**
* Calculate a size for the handle that will be large enough to show
* the CoolBar's drag affordance.
*
* @return The size that the handle has to be, based on the orientation
*/
private int getHandleSize() {
// Do we already have a 'cached' value?
if (orientation == SWT.HORIZONTAL && horizontalHandleSize != -1) {
return horizontalHandleSize;
}
if (orientation == SWT.VERTICAL && verticalHandleSize != -1) {
return verticalHandleSize;
}
// Must be the first time, calculate the value
CoolBar bar = new CoolBar (trim.getControl().getParent(), orientation);
CoolItem item = new CoolItem (bar, SWT.NONE);
Label ctrl = new Label (bar, SWT.PUSH);
ctrl.setText ("Button 1"); //$NON-NLS-1$
Point size = ctrl.computeSize (SWT.DEFAULT, SWT.DEFAULT);
Point ps = item.computeSize (size.x, size.y);
item.setPreferredSize (ps);
item.setControl (ctrl);
bar.pack ();
// OK, now the difference between the location of the CB and the
// location of the
Point bl = ctrl.getLocation();
Point cl = bar.getLocation();
// Toss them now...
ctrl.dispose();
item.dispose();
bar.dispose();
// The 'size' is the difference between the start of teh CoolBar and
// start of its first control
int length;
if (orientation == SWT.HORIZONTAL) {
length = bl.x - cl.x;
horizontalHandleSize = length;
}
else {
length = bl.y - cl.y;
verticalHandleSize = length;
}
return length;
}
/**
* Place a CoolBar / CoolItem / Control inside the current
* UI handle. These elements will maintain thier size based on
* the size of their 'parent' (this).
*
* @param parent
* @param orientation
*/
public void insertCoolBar(int orientation) {
// Clean up the previous info in case we've changed orientation
if (cb != null) {
ci.dispose();
PresentationUtil.removeDragListener(cb, dragListener);
cb.dispose();
}
// Create the necessary parts...
cb = new CoolBar(this, orientation | SWT.FLAT);
cb.setLocation(0,0);
ci = new CoolItem(cb, SWT.FLAT);
// Create a composite in order to get the handles to appear
Composite comp = new Composite(cb, SWT.NONE);
ci.setControl(comp);
}
/**
* Set the cursor to the four-way arrow to indicate that the
* trim can be dragged
*/
private void setDragCursor() {
Cursor dragCursor = toDrag.getDisplay().getSystemCursor(SWT.CURSOR_SIZEALL);
setCursor(dragCursor);
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean)
*/
public Point computeSize(int wHint, int hHint, boolean changed) {
Point ctrlPrefSize = trim.getControl().computeSize(wHint, hHint);
if (orientation == SWT.HORIZONTAL) {
return new Point(getHandleSize(), ctrlPrefSize.y);
}
// Must be vertical....
return new Point(ctrlPrefSize.x, getHandleSize());
}
/**
* Construct (if necessary) a context menu contribution item and return it. This
* is explicitly <code>public</code> so that trim elements can retrieve the item
* and add it into their own context menus if desired.
*
* @return The contribution item for the handle's context menu.
*/
public ContributionItem getDockingContribution() {
if (dockContributionItem == null) {
dockContributionItem = new ContributionItem() {
public void fill(Menu menu, int index) {
// populate from superclass
super.fill(menu, index);
// Add a 'Close' menu entry if the trim supports the operation
if (trim.isCloseable()) {
MenuItem closeItem = new MenuItem(menu, SWT.PUSH, index++);
closeItem.setText(WorkbenchMessages.TrimCommon_Close);
closeItem.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
handleCloseTrim();
}
public void widgetDefaultSelected(SelectionEvent e) {
}
});
new MenuItem(menu, SWT.SEPARATOR, index++);
}
// Test Hook: add a menu entry that brings up a dialog to allow
// testing with various GUI prefs.
// MenuItem closeItem = new MenuItem(menu, SWT.PUSH, index++);
// closeItem.setText("Change Preferences"); //$NON-NLS-1$
//
// closeItem.addSelectionListener(new SelectionListener() {
// public void widgetSelected(SelectionEvent e) {
// handleChangePreferences();
// }
//
// public void widgetDefaultSelected(SelectionEvent e) {
// }
// });
//
// new MenuItem(menu, SWT.SEPARATOR, index++);
// Create a cascading menu to allow the user to dock the trim
dockCascade = new MenuItem(menu, SWT.CASCADE, index++);
{
dockCascade.setText(WorkbenchMessages.TrimCommon_DockOn);
sidesMenu = new Menu(dockCascade);
radioButtons = new RadioMenu(sidesMenu, radioVal);
radioButtons.addMenuItem(WorkbenchMessages.TrimCommon_Top, new Integer(SWT.TOP));
radioButtons.addMenuItem(WorkbenchMessages.TrimCommon_Bottom, new Integer(SWT.BOTTOM));
radioButtons.addMenuItem(WorkbenchMessages.TrimCommon_Left, new Integer(SWT.LEFT));
radioButtons.addMenuItem(WorkbenchMessages.TrimCommon_Right, new Integer(SWT.RIGHT));
dockCascade.setMenu(sidesMenu);
}
// if the radioVal changes it means that the User wants to change the docking location
radioVal.addChangeListener(new IChangeListener() {
public void update(boolean changed) {
if (changed) {
handleShowOnChange();
}
}
});
// Provide Show / Hide trim capabilities
// showCascade = new MenuItem(menu, SWT.CASCADE, index++);
// {
// showCascade.setText(WorkbenchMessages.TrimCommon_ShowTrim);
//
// showMenu = new Menu(dockCascade);
//
// // Construct a 'hide/show' cascade from -all- the existing trim...
// List trimItems = layout.getAllTrim();
// Iterator d = trimItems.iterator();
// while (d.hasNext()) {
// IWindowTrim trimItem = (IWindowTrim) d.next();
// MenuItem item = new MenuItem(showMenu, SWT.CHECK);
// item.setText(trimItem.getDisplayName());
// item.setSelection(trimItem.getControl().getVisible());
// item.setData(trimItem);
//
// // TODO: Make this work...wire it off for now
// item.setEnabled(false);
//
// item.addSelectionListener(new SelectionListener() {
//
// public void widgetSelected(SelectionEvent e) {
// IWindowTrim trim = (IWindowTrim) e.widget.getData();
// layout.setTrimVisible(trim, !trim.getControl().getVisible());
// }
//
// public void widgetDefaultSelected(SelectionEvent e) {
// }
//
// });
// }
//
// showCascade.setMenu(showMenu);
// }
}
};
}
return dockContributionItem;
}
/**
* Test Hook: Bring up a dialog that allows the user to
* modify the trimdragging GUI preferences.
*/
// private void handleChangePreferences() {
// TrimDragPreferenceDialog dlg = new TrimDragPreferenceDialog(getShell());
// dlg.open();
// }
/**
* Handle the event generated when the "Close" item is
* selected on the context menu. This removes the associated
* trim and calls back to the IWidnowTrim to inform it that
* the User has closed the trim.
*/
private void handleCloseTrim() {
layout.removeTrim(trim);
trim.handleClose();
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Widget#dispose()
*/
public void dispose() {
if (radioButtons != null) {
radioButtons.dispose();
}
// tidy up...
removeControlListener(controlListener);
removeListener(SWT.MenuDetect, menuListener);
super.dispose();
}
/**
* Begins dragging the trim
*
* @param position initial mouse position
*/
protected void startDraggingTrim(Point position) {
Rectangle fakeBounds = new Rectangle(100000, 0,0,0);
DragUtil.performDrag(trim, fakeBounds, position, true);
}
/**
* Shows the popup menu for an item in the fast view bar.
*/
private void showDockTrimPopup(Point pt) {
Menu menu = dockMenuManager.createContextMenu(toDrag);
menu.setLocation(pt.x, pt.y);
menu.setVisible(true);
}
}