/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* 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:
* Del Myers -- initial API and implementation
*******************************************************************************/
package org.eclipse.zest.custom.sequence.visuals;
import java.util.Iterator;
import org.eclipse.draw2d.AbstractConnectionAnchor;
import org.eclipse.draw2d.AbstractHintLayout;
import org.eclipse.draw2d.ActionEvent;
import org.eclipse.draw2d.ActionListener;
import org.eclipse.draw2d.Animation;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.LayoutManager;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.zest.custom.sequence.figures.ActivationFigure;
import org.eclipse.zest.custom.sequence.figures.PlusMinusFigure;
import org.eclipse.zest.custom.sequence.visuals.interactions.ActivationHoverInteraction;
import org.eclipse.zest.custom.sequence.widgets.Activation;
import org.eclipse.zest.custom.sequence.widgets.Message;
import org.eclipse.zest.custom.sequence.widgets.PropertyChangeListener;
import org.eclipse.zest.custom.sequence.widgets.UMLItem;
import org.eclipse.zest.custom.sequence.widgets.internal.IWidgetProperties;
/**
* Visual representation of an activation.
* @author Del Myers
*/
public class ActivationVisual extends NodeVisualPart implements PropertyChangeListener {
private Label hover;
//private ExpandingInteraction expander;
private ActivationHoverInteraction highlighter;
private PlusMinusFigure expanderFigure;
private class WidgetLayoutConnectionAnchor extends AbstractConnectionAnchor {
private boolean source;
private Point animationHint;
private Rectangle targetBounds;
private ConnectionVisualPart part;
public WidgetLayoutConnectionAnchor(ConnectionVisualPart part, IFigure figure, boolean source) {
super(figure);
this.part = part;
this.source = source;
}
public Point getLocation(Point reference) {
Widget widget = part.getWidget();
if (widget instanceof Message) {
if (isActive()) {
Object layout = widget.getData(IWidgetProperties.LAYOUT);
if (layout instanceof PointList) {
if (((PointList)layout).size() < 2) {
return new Point(0,0);
}
Point endPoint = (source) ? ((PointList)layout).getFirstPoint() : ((PointList)layout).getLastPoint();
Point currentEnd = new Point(0,0);
try {
currentEnd = (source) ? part.getConnection().getPoints().getFirstPoint() : part.getConnection().getPoints().getLastPoint();
} catch (IndexOutOfBoundsException e) {
return new Point(0,0);
}
Point otherPoint = (source) ? ((PointList)layout).getLastPoint() : ((PointList)layout).getFirstPoint();
Rectangle finalLayout = (Rectangle) getWidget().getData(IWidgetProperties.LAYOUT);
Rectangle currentLayout = getFigure().getBounds();
if (finalLayout == null) {
animationHint = currentLayout.getCenter();
return animationHint;
}
if (!finalLayout.equals(targetBounds)) {
//cache for animation
targetBounds = finalLayout;
animationHint = currentEnd;
part.getConnection().invalidate();
return currentEnd;
} else {
if (!Animation.isAnimating()) {
return endPoint;
}
}
if (currentLayout.equals(finalLayout)) {
return endPoint;
}
if (!Animation.isAnimating()) {
return endPoint;
}
if (currentEnd.y == endPoint.y) {
return endPoint;
}
float scale = (float)currentLayout.height/(float)finalLayout.height;
int y = (int)(currentLayout.y + ((endPoint.y-finalLayout.y)*scale));
int x = currentLayout.x;
if (otherPoint.x > endPoint.x) {
x += currentLayout.width;
}
return new Point(x, y);
}
} else {
//return a point with the same y value as the other end.
Object layout = widget.getData(IWidgetProperties.LAYOUT);
if (layout instanceof PointList) {
try {
Point otherPoint = (source) ? ((PointList)layout).getLastPoint() : ((PointList)layout).getFirstPoint();
return new Point(0, otherPoint.y);
} catch (IndexOutOfBoundsException e) {
//do nothig
}
}
}
}
return new Point(0,0);
}
}
private class FlowingLayout extends AbstractHintLayout {
/* (non-Javadoc)
* @see org.eclipse.draw2d.AbstractLayout#calculatePreferredSize(org.eclipse.draw2d.IFigure, int, int)
*/
@SuppressWarnings("unchecked")
@Override
protected Dimension calculatePreferredSize(IFigure container, int hint,
int hint2) {
Dimension d = new Dimension();
for (Iterator<IFigure> i = ((Iterator<IFigure>)container.getChildren().iterator()); i.hasNext();) {
IFigure child = i.next();
Dimension childSize = child.getPreferredSize();
d.height += childSize.height;
if (d.width < childSize.width) {
d.width = childSize.width;
}
}
Dimension currentSize = container.getSize();
return new Dimension(Math.max(currentSize.width, d.width), Math.max(currentSize.height, d.height));
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.LayoutManager#layout(org.eclipse.draw2d.IFigure)
*/
@SuppressWarnings("unchecked")
public void layout(IFigure container) {
int top = 2;
Rectangle containerBounds = container.getBounds();
if (!container.isCoordinateSystem()) {
top += containerBounds.y;
}
int halfWidth = containerBounds.width/2;
for (Iterator<IFigure> i = container.getChildren().iterator(); i.hasNext();) {
IFigure child = i.next();
Dimension d = child.getPreferredSize();
int x = halfWidth - (d.width/2);
if (!container.isCoordinateSystem()) {
x += containerBounds.x;
}
Rectangle newBounds = new Rectangle(x, top, d.width, d.height);
top += d.height;
child.setBounds(newBounds);
}
}
}
/**
* @param item
* @param key
* @param parentFigure
*/
public ActivationVisual(UMLItem item, String key) {
super(item, key);
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.visuals.WidgetVisualPart#createFigure()
*/
@Override
public IFigure createFigures() {
IFigure figure = new ActivationFigure();
LayoutManager layout = new FlowingLayout();
figure.setLayoutManager(layout);
hover = new Label();
figure.setToolTip(hover);
return figure;
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.visuals.WidgetVisualPart#refreshVisuals()
*/
@Override
public void refreshVisuals() {
Object layout = getWidget().getData(IWidgetProperties.LAYOUT);
if (layout == null) {
getFigure().setVisible(false);
} else {
getFigure().setVisible(true);
}
getFigure().setBackgroundColor(getActivation().getBackground());
String toolTipText = getWidget().getTooltipText();
if (toolTipText == null) {
toolTipText = getActivation().getText();
}
hover.setText(toolTipText);
if (getActivation().getImage() != null) {
hover.setIcon(getActivation().getImage());
}
getFigure().setForegroundColor(getActivation().getForeground());
// if (!getActivation().hasChildren()) {
//
// } else {
// getFigure().setForegroundColor(ZestUMLColors.ColorExpandable.getColor());
// }
if (getFigure().getBounds() == null || getFigure().getBounds().isEmpty()) {
if (getActivation().getSourceCall() != null) {
Activation callingActivation = getActivation().getSourceCall().getSource();
if (callingActivation != null) {
WidgetVisualPart callerVisuals = (WidgetVisualPart) callingActivation.getData(MessageBasedSequenceVisuals.VISUAL_KEY);
if (callerVisuals != null && callerVisuals.isActive()) {
Rectangle bounds = callerVisuals.getFigure().getBounds();
getFigure().setBounds(bounds.getCopy());
}
}
}
}
boolean highlight = getActivation().getData("pin") == Boolean.TRUE ||
getActivation().getData(IWidgetProperties.HIGHLIGHT) == Boolean.TRUE;
if (highlight) {
((ActivationFigure)getFigure()).setLineWidth(2);
} else {
((ActivationFigure)getFigure()).setLineWidth(1);
}
resetExpander();
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.widgets.PropertyChangeListener#propertyChanged(java.lang.Object, java.lang.String, java.lang.Object, java.lang.Object)
*/
public void propertyChanged(Object source, String property, Object oldValue, Object newValue) {
if (getChartVisuals().refreshing) return; //early return
if (IWidgetProperties.LAYOUT.equals(property)) {
if (newValue == null) return;
//getFigure().setBounds(((Rectangle)newValue).getCopy());
getParentFigure().setConstraint(getFigure(), ((Rectangle)newValue).getCopy());
updateExpander();
getFigure().setVisible(true);
//getParentFigure().getLayoutManager().layout(getParentFigure());
//getFigure().invalidate();
} else if (IWidgetProperties.HIGHLIGHT.equals(property) || "pin".equals(property)) {
boolean highlight = Boolean.TRUE.equals(getActivation().getData("pin")) ||
getActivation().isHighlighted();
if (highlight) {
((ActivationFigure)getFigure()).setLineWidth(2);
} else {
((ActivationFigure)getFigure()).setLineWidth(1);
}
} else if (IWidgetProperties.SUB_CALL.equals(property)) {
//Activation a = (Activation) source;
resetExpander();
// if (a.hasChildren()) {
// if (expander.getPart() == null) {
// expander.hookInteraction(this);
// refreshVisuals();
// }
// } else {
// if (expander.getPart() != null) {
// expander.unhookInteraction();
// refreshVisuals();
// }
// }
} else if (IWidgetProperties.BACKGROUND_COLOR.equals(property)) {
if ((getWidget().getBackground()) != null) {
getFigure().setBackgroundColor(((getWidget()).getBackground()));
} else if (getParentFigure() != null){
getFigure().setBackgroundColor(getParentFigure().getBackgroundColor());
}
} else if (IWidgetProperties.FOREGROUND_COLOR.equals(property)) {
if (getWidget().getForeground() != null) {
getFigure().setForegroundColor(((getWidget()).getForeground()));
} else if (getParentFigure() != null){
getFigure().setForegroundColor(getParentFigure().getForegroundColor());
}
} else if (IWidgetProperties.EXPANDED.equals(property)) {
updateExpander();
} else if (IWidgetProperties.TOOLTIP.equals(property)) {
String toolTipText = getWidget().getTooltipText();
if (toolTipText == null) {
toolTipText = getActivation().getText();
}
hover.setText(toolTipText);
} else if (IWidgetProperties.MESSAGE.equals(property)) {
resetExpander();
}
}
private IFigure getParentFigure() {
return getFigure().getParent();
}
/**
* @return
*/
private Activation getActivation() {
return (Activation) getWidget();
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.visuals.WidgetVisualPart#activate()
*/
@Override
public void activate() {
super.activate();
// if (expander == null) {
// expander = new ExpandingInteraction();
// }
if (highlighter == null) {
highlighter = new ActivationHoverInteraction();
}
// if (getActivation().hasChildren()) {
// expander.hookInteraction(this);
// }
highlighter.hookInteraction(this);
//selector.hookInteraction(this);
getActivation().addPropertyChangeListener(this);
Message[] messages = getActivation().getMessages();
if (messages.length > 0 && messages[0].getTarget() == getActivation()) {
Activation source = messages[0].getSource();
Object layout = source.getData(IWidgetProperties.LAYOUT);
if (layout instanceof Rectangle) {
getFigure().setBounds(((Rectangle)layout).getCopy());
}
}
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.visuals.WidgetVisualPart#deactivate()
*/
@Override
public void deactivate() {
super.deactivate();
getFigure().setSize(0,0);
//expander.unhookInteraction();
resetExpander();
highlighter.unhookInteraction();
getActivation().removePropertyChangeListener(this);
if (!getActivation().isDisposed())
getActivation().setData(IWidgetProperties.LAYOUT, null);
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.visuals.NodeVisualPart#getSourceAnchor(org.eclipse.mylar.zest.custom.sequence.visuals.ConnectionVisualPart)
*/
@Override
public ConnectionAnchor getSourceAnchor(ConnectionVisualPart part) {
return new WidgetLayoutConnectionAnchor(part, getFigure(), true);
}
/* (non-Javadoc)
* @see org.eclipse.mylar.zest.custom.sequence.visuals.NodeVisualPart#getTargetAnchor(org.eclipse.mylar.zest.custom.sequence.visuals.ConnectionVisualPart)
*/
@Override
public ConnectionAnchor getTargetAnchor(ConnectionVisualPart part) {
return new WidgetLayoutConnectionAnchor(part, getFigure(), false);
}
/**
* Resets the expander visibility based on whether the activation in this visual has children, and the visual
* is active.
*/
private void resetExpander() {
Activation a = getActivation();
boolean addExpander = a.hasChildren() && isActive() && a.isVisible() && !a.isHidden();
if (addExpander) {
if (expanderFigure == null) {
expanderFigure = new PlusMinusFigure(9);
expanderFigure.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent event) {
Activation a = getActivation();
a.setExpanded(!a.isExpanded());
}
});
getFigure().add(expanderFigure);
}
// int index = getParentFigure().getChildren().indexOf(getFigure());
// int expanderIndex = getParentFigure().getChildren().indexOf(expanderFigure);
// //@tag zest.sequence.performance : draw2d makes this operation f^2*a where f is the total number of figures, and a is the number of activations. Not good.
// if (expanderIndex != index+1) {
// if (index+1>=getParentFigure().getChildren().size()) {
// } else {
// getFigure().add(expanderFigure, index+1);
// }
// }
updateExpander();
} else {
if (expanderFigure != null && expanderFigure.getParent() != null) {
expanderFigure.getParent().remove(expanderFigure);
}
expanderFigure = null;
}
}
/**
* Updates the expander figure according to match the visual state of this visual.
*/
private void updateExpander() {
if (expanderFigure == null) return;
Activation a = getActivation();
expanderFigure.setSelected(!a.isExpanded());
// Rectangle bounds = (Rectangle) getParentFigure().getLayoutManager().getConstraint(getFigure());
//
// if (bounds == null) {
// bounds = getFigure().getBounds();
// }
// if (bounds != null) {
// expanderFigure.getParent().setConstraint(expanderFigure, new Rectangle(
// bounds.x-1,
// bounds.y+2,
// 9,
// 9
// ));
// }
}
/* (non-Javadoc)
* @see org.eclipse.zest.custom.sequence.visuals.WidgetVisualPart#installFigures()
*/
@Override
protected void installFigures() {
super.installFigures();
// if (expanderFigure != null) {
// if (expanderFigure.getParent() != null) {
// IFigure eParent = expanderFigure.getParent();
// eParent.remove(expanderFigure);
// eParent.add(expanderFigure);
// }
// }
}
}