blob: 43a3e08d583b6b68c91fa1254e53cf8cfb504cd1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 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.gef.internal.ui.palette.editparts;
import java.beans.PropertyChangeEvent;
import java.util.Iterator;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.MenuListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.draw2d.ActionEvent;
import org.eclipse.draw2d.ActionListener;
import org.eclipse.draw2d.Border;
import org.eclipse.draw2d.BorderLayout;
import org.eclipse.draw2d.ButtonBorder;
import org.eclipse.draw2d.ButtonModel;
import org.eclipse.draw2d.ChangeEvent;
import org.eclipse.draw2d.ChangeListener;
import org.eclipse.draw2d.Clickable;
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.StackLayout;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.palette.PaletteEntry;
import org.eclipse.gef.palette.PaletteListener;
import org.eclipse.gef.palette.PaletteStack;
import org.eclipse.gef.palette.ToolEntry;
import org.eclipse.gef.ui.actions.SetActivePaletteToolAction;
import org.eclipse.gef.ui.palette.PaletteViewer;
import org.eclipse.gef.ui.palette.editparts.PaletteEditPart;
/**
* The EditPart for a PaletteStack to be used on the toolbar.
*
* @author Whitney Sorenson
* @since 3.0
*/
public class PaletteStackEditPart extends PaletteEditPart implements
IPaletteStackEditPart {
private static final Dimension EMPTY_DIMENSION = new Dimension(0, 0);
// listen to changes of clickable tool figure
private ChangeListener clickableListener = new ChangeListener() {
public void handleStateChanged(ChangeEvent event) {
// RAP [am] no mouse over
/*
* if
* (event.getPropertyName().equals(ButtonModel.MOUSEOVER_PROPERTY))
* arrowFigure.getModel().setMouseOver(
* activeFigure.getModel().isMouseOver()); else
*/if (event.getPropertyName().equals(ButtonModel.ARMED_PROPERTY))
arrowFigure.getModel().setArmed(
activeFigure.getModel().isArmed());
}
};
// listen to changes of arrow figure
private ChangeListener clickableArrowListener = new ChangeListener() {
public void handleStateChanged(ChangeEvent event) {
// RAP [am] no mouse over
// if
// (event.getPropertyName().equals(ButtonModel.MOUSEOVER_PROPERTY))
// activeFigure.getModel().setMouseOver(
// arrowFigure.getModel().isMouseOver());
// RAPEND: [am]
if (event.getPropertyName().equals(ButtonModel.ARMED_PROPERTY))
activeFigure.getModel().setArmed(
arrowFigure.getModel().isArmed());
}
};
// listen to see if arrow is pressed
private ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
openMenu();
}
};
// listen to see if active tool is changed in palette
private PaletteListener paletteListener = new PaletteListener() {
public void activeToolChanged(PaletteViewer palette, ToolEntry tool) {
if (getStack().getChildren().contains(tool)) {
if (!arrowFigure.getModel().isSelected())
arrowFigure.getModel().setSelected(true);
if (!getStack().getActiveEntry().equals(tool))
getStack().setActiveEntry(tool);
} else
arrowFigure.getModel().setSelected(false);
}
};
private Clickable activeFigure;
private RolloverArrow arrowFigure;
private Figure contentsFigure;
private Menu menu;
/**
* Creates a new PaletteStackEditPart with the given PaletteStack as its
* model.
*
* @param model
* the PaletteStack to associate with this EditPart.
*/
public PaletteStackEditPart(PaletteStack model) {
super(model);
}
/**
* @see org.eclipse.gef.EditPart#activate()
*/
public void activate() {
// in case the model is out of sync
checkActiveEntrySync();
getPaletteViewer().addPaletteListener(paletteListener);
super.activate();
}
/**
* Called when the active entry has changed.
*
* @param oldValue
* the old model value (can be null)
* @param newValue
* the new model value (can be null)
*/
private void activeEntryChanged(Object oldValue, Object newValue) {
GraphicalEditPart part = null;
Clickable clickable = null;
if (newValue != null) {
part = (GraphicalEditPart) getViewer().getEditPartRegistry().get(
newValue);
clickable = (Clickable) part.getFigure();
clickable.setVisible(true);
clickable.addChangeListener(clickableListener);
activeFigure = clickable;
} else {
activeFigure = null;
}
if (oldValue != null) {
part = (GraphicalEditPart) getViewer().getEditPartRegistry().get(
oldValue);
// if part is null, its no longer a child.
if (part != null) {
clickable = (Clickable) part.getFigure();
clickable.setVisible(false);
clickable.removeChangeListener(clickableListener);
}
}
}
private void checkActiveEntrySync() {
if (activeFigure == null)
activeEntryChanged(null, getStack().getActiveEntry());
}
/**
* @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure()
*/
public IFigure createFigure() {
Figure figure = new Figure() {
public Dimension getPreferredSize(int wHint, int hHint) {
if (PaletteStackEditPart.this.getChildren().isEmpty())
return EMPTY_DIMENSION;
return super.getPreferredSize(wHint, hHint);
}
};
figure.setLayoutManager(new BorderLayout());
contentsFigure = new Figure();
StackLayout stackLayout = new StackLayout();
// make it so the stack layout does not allow the invisible figures to
// contribute
// to its bounds
stackLayout.setObserveVisibility(true);
contentsFigure.setLayoutManager(stackLayout);
figure.add(contentsFigure, BorderLayout.CENTER);
arrowFigure = new RolloverArrow();
arrowFigure.addChangeListener(clickableArrowListener);
arrowFigure.addActionListener(actionListener);
figure.add(arrowFigure, BorderLayout.RIGHT);
return figure;
}
/**
* @see org.eclipse.gef.EditPart#deactivate()
*/
public void deactivate() {
if (activeFigure != null)
activeFigure.removeChangeListener(clickableListener);
arrowFigure.removeActionListener(actionListener);
arrowFigure.removeChangeListener(clickableArrowListener);
getPaletteViewer().removePaletteListener(paletteListener);
super.deactivate();
}
/**
* @see org.eclipse.gef.EditPart#eraseTargetFeedback(org.eclipse.gef.Request)
*/
public void eraseTargetFeedback(Request request) {
Iterator children = getChildren().iterator();
while (children.hasNext()) {
PaletteEditPart part = (PaletteEditPart) children.next();
part.eraseTargetFeedback(request);
}
super.eraseTargetFeedback(request);
}
/**
* @see org.eclipse.gef.GraphicalEditPart#getContentPane()
*/
public IFigure getContentPane() {
return contentsFigure;
}
private PaletteStack getStack() {
return (PaletteStack) getModel();
}
/**
* Opens the menu to display the choices for the active entry.
*/
public void openMenu() {
MenuManager menuManager = new MenuManager();
Iterator children = getChildren().iterator();
PaletteEditPart part = null;
PaletteEntry entry = null;
while (children.hasNext()) {
part = (PaletteEditPart) children.next();
entry = (PaletteEntry) part.getModel();
menuManager
.add(new SetActivePaletteToolAction(getPaletteViewer(),
entry.getLabel(), entry.getSmallIcon(), getStack()
.getActiveEntry().equals(entry),
(ToolEntry) entry));
}
menu = menuManager.createContextMenu(getPaletteViewer().getControl());
// make the menu open below the figure
Rectangle figureBounds = getFigure().getBounds().getCopy();
getFigure().translateToAbsolute(figureBounds);
Point menuLocation = getPaletteViewer().getControl().toDisplay(
figureBounds.getBottomLeft().x, figureBounds.getBottomLeft().y);
// remove feedback from the arrow Figure and children figures
arrowFigure.getModel().setMouseOver(false);
eraseTargetFeedback(new Request(RequestConstants.REQ_SELECTION));
menu.setLocation(menuLocation);
menu.addMenuListener(new StackMenuListener(menu, getViewer()
.getControl().getDisplay()));
menu.setVisible(true);
}
/**
* @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
if (event.getPropertyName().equals(PaletteStack.PROPERTY_ACTIVE_ENTRY))
activeEntryChanged(event.getOldValue(), event.getNewValue());
else
super.propertyChange(event);
}
/**
* @see org.eclipse.gef.editparts.AbstractEditPart#refreshChildren()
*/
protected void refreshChildren() {
super.refreshChildren();
checkActiveEntrySync();
Iterator children = getChildren().iterator();
while (children.hasNext()) {
PaletteEditPart editPart = (PaletteEditPart) children.next();
if (!editPart.getFigure().equals(activeFigure))
editPart.getFigure().setVisible(false);
}
}
/**
* @see org.eclipse.gef.EditPart#showTargetFeedback(org.eclipse.gef.Request)
*/
public void showTargetFeedback(Request request) {
// if menu is showing, don't show feedback. this is a fix
// for the occasion when show is called after forced erase
if (menu != null && !menu.isDisposed() && menu.isVisible())
return;
Iterator children = getChildren().iterator();
while (children.hasNext()) {
PaletteEditPart part = (PaletteEditPart) children.next();
part.showTargetFeedback(request);
}
super.showTargetFeedback(request);
}
public PaletteEditPart getActiveEntry() {
return (PaletteEditPart) getViewer().getEditPartRegistry().get(
getStack().getActiveEntry());
}
}
class StackMenuListener implements MenuListener {
private Menu menu;
private Display d;
/**
* Creates a new listener to listen to the menu that it used to select the
* active tool on a stack. Disposes the stack with an asyncExec after hidden
* is called.
*/
StackMenuListener(Menu menu, Display d) {
this.menu = menu;
this.d = d;
}
/**
* @see org.eclipse.swt.events.MenuListener#menuShown(org.eclipse.swt.events.MenuEvent)
*/
public void menuShown(MenuEvent e) {
}
/**
* @see org.eclipse.swt.events.MenuListener#menuHidden(org.eclipse.swt.events.MenuEvent)
*/
public void menuHidden(MenuEvent e) {
d.asyncExec(new Runnable() {
public void run() {
if (menu != null) {
if (!menu.isDisposed())
menu.dispose();
menu = null;
}
}
});
}
}
class RolloverArrow extends Clickable {
private final Border BORDER_TOGGLE = new ButtonBorder(
ButtonBorder.SCHEMES.TOOLBAR());
/**
* Creates a new Clickable that paints a triangle figure.
*/
RolloverArrow() {
super();
setRolloverEnabled(true);
setBorder(BORDER_TOGGLE);
setBackgroundColor(ColorConstants.black());
setOpaque(false);
setPreferredSize(11, -1);
}
/**
* @return false so that the focus rectangle is not drawn.
*/
public boolean hasFocus() {
return false;
}
public void paintFigure(Graphics graphics) {
Rectangle rect = getClientArea();
graphics.translate(getLocation());
// fill the arrow
int[] points = new int[8];
points[0] = 3;
points[1] = rect.height / 2;
points[2] = 8;
points[3] = rect.height / 2;
points[4] = 5;
points[5] = 3 + rect.height / 2;
points[6] = 3;
points[7] = rect.height / 2;
graphics.fillPolygon(points);
graphics.translate(getLocation().getNegated());
}
}