/*******************************************************************************
* 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.figures;
import org.eclipse.draw2d.Border;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.Shape;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontMetrics;
/**
* A figure that represents a sequence class. Sequence classes have
* labels for the class name and a stereo type. The figure drawn
* depends on the type of class. This type can either be the
* standard CLASS, in which case a box with an underlined
* class name is drawn, or it can be ACTOR, in which case a
* stick figure is drawn.
*
* Children cannot be added to the sequence class, nor can
* a layout be set on it. Trying to do so will not cause an
* error, but it will have no effect.
*
* @author Del Myers
*
*/
public class SequenceClassFigure extends Figure {
public static final int CLASS = 0;
public static final int COLLECTION = 1;
public static final int ACTOR = 2;
public static final int BOUNDARY = 3;
public static final int CONTROL = 4;
public static final int ENTITY = 5;
public static final int DATA_STORE = 6;
public static final int PACKAGE = 7;
private final int MINIMUM_SIZE = 16;
private int type;
private Shape classFigure;
private Label classLabel;
private Label stereoTypeLabel;
private class UnderlineLabel extends Label {
private String subStringText;
/* (non-Javadoc)
* @see org.eclipse.draw2d.Label#paintFigure(org.eclipse.draw2d.Graphics)
*/
protected void paintFigure(Graphics graphics) {
super.paintFigure(graphics);
Dimension size = getSubStringTextSize();
Point location = getTextLocation().getCopy();
Border b = getBorder();
if (b != null) {
location.x -= b.getInsets(this).left;
location.y -= b.getInsets(this).top;
}
Rectangle r = getClientArea();
graphics.translate(r.x, r.y);
Rectangle c =r.getCopy();
translateToRelative(c);
//graphics.setClip(c);
graphics.setLineWidth(2);
graphics.drawLine(location.x, location.y+size.height, location.x+size.width, location.y+size.height);
graphics.translate(-r.x, -r.y);
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#getBounds()
*/
public Rectangle getBounds() {
Rectangle b = super.getBounds().getCopy();
b.height+=6;
return b;
}
public String getSubStringText() {
// if (subStringText != null)
// return subStringText;
String text = getText();
subStringText = getText();
int dot = text.lastIndexOf('.');
String className = "";
if (dot >=0 && dot < text.length()) {
className = text.substring(dot+1);
}
int widthShrink = getPreferredSize().width - getSize().width;
if (widthShrink <= 0)
return subStringText;
Dimension effectiveSize = getTextSize().getExpanded(-widthShrink, 0);
Font currentFont = getFont();
int dotsWidth = FigureUtilities.getTextWidth("....", currentFont);
if (effectiveSize.width < dotsWidth)
effectiveSize.width = dotsWidth;
int subStringLength = getLargestSubstringConfinedTo(text,
currentFont,
effectiveSize.width - dotsWidth);
if (text.length() > subStringLength) {
if (!"".equals(className)) {
if (className.length() >= subStringLength) {
subStringLength = getLargestSubstringConfinedTo(className,
currentFont,
effectiveSize.width - dotsWidth);
subStringText = new String(className.substring(0, subStringLength) + "...");
} else {
subStringText = className;
}
} else {
subStringText = new String(text.substring(0, subStringLength) + "...");
}
} else {
subStringText = text;
}
return subStringText;
}
int getLargestSubstringConfinedTo(String s, Font f, int availableWidth) {
FontMetrics metrics = FigureUtilities.getFontMetrics(f);
int min, max;
float avg = metrics.getAverageCharWidth();
min = 0;
max = s.length() + 1;
//The size of the current guess
int guess = 0,
guessSize = 0;
while ((max - min) > 1) {
//Pick a new guess size
// New guess is the last guess plus the missing width in pixels
// divided by the average character size in pixels
guess = guess + (int)((availableWidth - guessSize) / avg);
if (guess >= max) guess = max - 1;
if (guess <= min) guess = min + 1;
//Measure the current guess
guessSize = FigureUtilities.getTextExtents(s.substring(0, guess), f).width;
if (guessSize < availableWidth)
//We did not use the available width
min = guess;
else
//We exceeded the available width
max = guess;
}
return min;
}
}
public SequenceClassFigure(int type) {
super();
this.type = type;
classFigure = createClassFigure();
classLabel = new UnderlineLabel();
classLabel.setBorder(new SpacerBorder(3,3,3,3));
stereoTypeLabel = new Label();
this.type = type;
}
protected Dimension calculatePreferredSize(int wHint, int hHint) {
updateInternalFigures();
Rectangle classTextSize = classLabel.getTextBounds();
Rectangle stereoTextSize = stereoTypeLabel.getTextBounds();
//don't show the stereotype if it is empty
if (stereoTypeLabel.getText() == null || (stereoTypeLabel.getText().trim().length()==0)) {
stereoTextSize.height = 0;
}
classTextSize.expand(classLabel.getBorder().getInsets(classLabel));
int labelHeight = classTextSize.height + stereoTextSize.height+5;
int height = labelHeight;
switch (getType()) {
case ACTOR:
case BOUNDARY:
case CONTROL:
case ENTITY:
case DATA_STORE:
height += MINIMUM_SIZE;
}
int width = Math.max(classTextSize.width, stereoTextSize.width);
if (hHint > height) {
height = hHint;
}
if (wHint > width) {
width = wHint;
}
return new Dimension(width, height);
}
/**
*
*/
private void updateInternalFigures() {
classFigure.setBackgroundColor(getBackgroundColor());
classFigure.setForegroundColor(getForegroundColor());
classFigure.setFont(getFont());
classLabel.setBackgroundColor(getBackgroundColor());
classLabel.setForegroundColor(getForegroundColor());
classLabel.setFont(getFont());
stereoTypeLabel.setBackgroundColor(getBackgroundColor());
stereoTypeLabel.setForegroundColor(getForegroundColor());
stereoTypeLabel.setFont(getFont());
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#getPreferredSize(int, int)
*/
@Override
public Dimension getPreferredSize(int wHint, int hHint) {
Dimension localSize = calculatePreferredSize(wHint, hHint);
updateInternalFigures();
if (getLayoutManager() != null) {
Dimension d = getLayoutManager().getPreferredSize(this, wHint, hHint);
if (d != null) {
localSize.height += d.height;
if (d.width > localSize.width) {
localSize.width = localSize.width;
}
}
}
return localSize;
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#getMaximumSize()
*/
@Override
public Dimension getMaximumSize() {
return super.getMaximumSize();
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#getMinimumSize(int, int)
*/
@Override
public Dimension getMinimumSize(int hint, int hint2) {
Dimension d = super.getMinimumSize(hint, hint2);
//put the minimum size on the top of this area
d.height+=MINIMUM_SIZE;
if (d.width < MINIMUM_SIZE) {
d.width = MINIMUM_SIZE;
}
return d;
}
private void layoutLocalFigures() {
updateInternalFigures();
Dimension classTextSize = classLabel.getPreferredSize();
Dimension stereoTextSize = stereoTypeLabel.getPreferredSize();
Rectangle bounds = getBounds().getCopy();
Rectangle clientArea = getClientArea();
bounds.y += clientArea.height;
bounds.height -= clientArea.height;
//don't show the stereotype if it is empty
if (stereoTypeLabel.getText() == null || (stereoTypeLabel.getText().trim().length()==0)) {
stereoTextSize.height = 0;
}
int labelHeight = classTextSize.height + stereoTextSize.height+5;
switch(getType()) {
case ACTOR:
case BOUNDARY:
case CONTROL:
case ENTITY:
case DATA_STORE:
//place the label at the bottom, and
//give the rest of the room to the class figure.
if (bounds.height - MINIMUM_SIZE < labelHeight) {
labelHeight = 0;
classTextSize.height = 0;
}
int actorHeight = bounds.height - labelHeight;
//keep the aspect ratio, and center the actor.
classFigure.setBounds(new Rectangle(bounds.x + bounds.width/2 - actorHeight/2, bounds.y, actorHeight, actorHeight));
classLabel.setBounds(new Rectangle(bounds.x, bounds.y+actorHeight, bounds.width, classTextSize.height));
stereoTypeLabel.setBounds(new Rectangle(bounds.x, bounds.y+actorHeight+classTextSize.height+5, bounds.width, stereoTextSize.height));
break;
default:
//center the labels inside the box.
classFigure.setBounds(getBounds().getCopy());
bounds = classFigure.getClientArea();
classLabel.setBounds(new Rectangle(bounds.x, bounds.y + bounds.height/2 - labelHeight/2, bounds.width, classTextSize.height));
stereoTypeLabel.setBounds(new Rectangle(bounds.x, classLabel.getBounds().y + classTextSize.height + 5, bounds.width, stereoTextSize.height));
}
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#paintClientArea(org.eclipse.draw2d.Graphics)
*/
@Override
protected void paintFigure(Graphics graphics) {
super.paintFigure(graphics);
layoutLocalFigures();
classFigure.paint(graphics);
classLabel.paint(graphics);
stereoTypeLabel.paint(graphics);
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#paintChildren(org.eclipse.draw2d.Graphics)
*/
@Override
protected void paintChildren(Graphics graphics) {
if (getClientArea().isEmpty()) {
return;
}
super.paintChildren(graphics);
}
private Shape createClassFigure() {
switch (getType()) {
case COLLECTION:
return new CollectionFigure();
case BOUNDARY:
return new CircleFigure();
case CONTROL:
return new ControlFigure();
case ENTITY:
return new EntityFigure();
case ACTOR:
return new ActorFigure();
case PACKAGE:
return new PackageFigure();
case DATA_STORE:
return new CylinderFigure();
case CLASS:
default:
return new RectangleFigure();
}
}
/**
* @return the type
*/
public int getType() {
return type;
}
public void setClassName(String text) {
classLabel.setText(text);
}
public void setStereoType(String text) {
if (text != null && !"".equals(text))
stereoTypeLabel.setText("�"+text+"�");
else
stereoTypeLabel.setText("");
}
/**
*Figures cannont be added to this figure. Trying to do so
*will have no effect.
*/
public final void add(IFigure figure, Object constraint, int index) {
super.add(figure, constraint, index);
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#getClientArea(org.eclipse.draw2d.geometry.Rectangle)
*/
@Override
public Rectangle getClientArea(Rectangle rect) {
Rectangle bounds = getBounds().getCopy();
Dimension clientSize = new Dimension();
Rectangle result = new Rectangle();
clientSize.width = bounds.width;
if (getLayoutManager() != null) {
Dimension preferred = getLayoutManager().getPreferredSize(this, bounds.width, -1);
clientSize.width = Math.max(bounds.width, preferred.width);
clientSize.height = preferred.height;
if (bounds.height - clientSize.height < MINIMUM_SIZE) {
Dimension minimum = getLayoutManager().getMinimumSize(this, bounds.width, -1);
if (bounds.height - minimum.height < MINIMUM_SIZE) {
clientSize.height = 0;
}
}
}
if (!isCoordinateSystem()) {
result.setLocation(bounds.getLocation());
}
result.setSize(clientSize);
return result;
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#useLocalCoordinates()
*/
protected boolean useLocalCoordinates() {
return false;
}
public void setLineWidth(int width) {
classFigure.setLineWidth(width);
repaint();
}
public Label getLabel() {
return classLabel;
}
/* (non-Javadoc)
* @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle)
*/
@Override
public void setBounds(Rectangle rect) {
// TODO Auto-generated method stub
super.setBounds(rect);
}
}