/*
* Copyright (c) 2005-2006, David Benson
*
* See LICENSE file in distribution for licensing details of this source file
*/
package com.jgraph.example.portlabels;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import org.jgraph.JGraph;
import org.jgraph.graph.CellView;
import org.jgraph.graph.CellViewRenderer;
import org.jgraph.graph.GraphConstants;
import org.jgraph.graph.GraphModel;
import org.jgraph.graph.VertexRenderer;
import org.jgraph.graph.VertexView;
/**
*
*/
public class PortLabelVertexView extends VertexView {
/**
* the renderer for this view
*/
protected static WrapperPortLabelRenderer renderer = new WrapperPortLabelRenderer();
protected CellView[] ports;
/**
* Creates new instance of <code>InstanceView</code>.
*/
public PortLabelVertexView() {
super();
}
/**
* Creates new instance of <code>InstanceView</code> for the specified
* graph cell.
*
* @param arg0
* a graph cell to create view for
*/
public PortLabelVertexView(Object arg0) {
super(arg0);
}
/*
* (non-Javadoc)
*
* @see org.jgraph.graph.AbstractCellView#getRenderer()
*/
public CellViewRenderer getRenderer() {
return renderer;
}
public static class WrapperPortLabelRenderer extends JPanel implements
CellViewRenderer {
private transient static JLabel label = new JLabel();
private transient static PortLabelVertexRenderer portLabelRenderer = new PortLabelVertexRenderer();
/**
* Cache the current graph for drawing
*/
protected transient JGraph graph = null;
transient protected Color gradientColor = null;
/** Cached hasFocus and selected value. */
transient protected boolean hasFocus, selected, preview;
/** Cached default foreground and default background. */
transient protected Color defaultForeground, defaultBackground;
/**
* Constructs a renderer that may be used to render vertices.
*/
public WrapperPortLabelRenderer() {
super(new BorderLayout());
this.add(portLabelRenderer, BorderLayout.CENTER);
this.add(label, BorderLayout.SOUTH);
defaultForeground = UIManager.getColor("Tree.textForeground");
defaultBackground = UIManager.getColor("Tree.textBackground");
label.setOpaque(false);
}
public Component getRendererComponent(JGraph graph, CellView view,
boolean sel, boolean focus, boolean preview) {
portLabelRenderer.getRendererComponent(graph, view, sel, focus,
preview);
label.setText(view.getCell().toString());
this.graph = graph;
this.selected = sel;
this.preview = preview;
this.hasFocus = focus;
installAttributes(view);
return this;
}
public void paint(Graphics g) {
if (gradientColor != null && !preview) {
setOpaque(false);
Graphics2D g2d = (Graphics2D) g;
g2d.setPaint(new GradientPaint(0, 0, getBackground(),
getWidth(), getHeight(), gradientColor, true));
g2d.fillRect(0, 0, getWidth(), getHeight());
}
super.paint(g);
}
public Point2D getPerimeterPoint(VertexView view, Point2D source, Point2D p) {
return portLabelRenderer.getPerimeterPoint(view, source, p);
}
protected void installAttributes(CellView view) {
Map attributes = view.getAllAttributes();
label.setVerticalAlignment(GraphConstants.getVerticalAlignment(attributes));
label.setHorizontalAlignment(GraphConstants.getHorizontalAlignment(attributes));
label.setVerticalTextPosition(GraphConstants.getVerticalTextPosition(attributes));
label.setHorizontalTextPosition(GraphConstants.getHorizontalTextPosition(attributes));
label.setFont(GraphConstants.getFont(attributes));
Color foreground = GraphConstants.getForeground(attributes);
label.setForeground((foreground != null) ? foreground : defaultForeground);
portLabelRenderer.installAttributes(view);
Color background = null;
if (graph != null) {
background = graph.getBackground();
}
setBackground((background != null) ? background : defaultBackground);
}
}
/**
* The renderer class for instance view.
*/
public static class PortLabelVertexRenderer extends VertexRenderer {
protected CellView[] ports = null;
protected FontMetrics fontMetrics = null;
public static transient int PORTLABELSPACING = 8;
public static transient int MINIMUMHORIZONTALSPACING = 60;
public static transient int MINIMUMVERTICALSPACING = 40;
/**
* The vertical distance added to the label height to make the color backdrop extend
* beyond the label text
*/
public static transient int COLORHEIGHTBUFFER = 4;
/**
* The maximum width of a label, any label more than this value in
* width
*/
public static transient int MAXLABELWIDTH = 150;
/*
* (non-Javadoc)
*
* @see java.awt.Component#paint(java.awt.Graphics)
*/
public void paint(Graphics g) {
super.paint(g);
g.setColor(getForeground());
paintPortLabels(g);
}
public Component getRendererComponent(JGraph graph, CellView view,
boolean sel, boolean focus, boolean preview) {
// Do not display the label, the wrapper does this
setText(null);
// Finds the ports and install them into the renderer
Graphics2D g = (Graphics2D) graph.getGraphics();
fontMetrics = g.getFontMetrics();
Object cell = view.getCell();
GraphModel model = graph.getModel();
int childCount = model.getChildCount(cell);
List result = new ArrayList(childCount);
for (int i = 0; i < childCount; i++) {
Object child = model.getChild(cell, i);
if (model.isPort(child)) {
CellView portView = graph.getGraphLayoutCache().getMapping(
child, false);
if (portView != null) {
result.add(portView);
}
}
}
ports = new CellView[result.size()];
result.toArray(ports);
Component c = super.getRendererComponent(graph, view, sel, focus, preview);
// Do not display the label, the wrapper does this
setText(null);
return c;
}
/**
* Install the attributes of specified cell in this renderer instance. This
* means, retrieve every published key from the cells hashtable and set
* global variables or superclass properties accordingly.
*
* @param view
* the cell view to retrieve the attribute values from.
*/
protected void installAttributes(CellView view) {
Map map = view.getAllAttributes();
setIcon(GraphConstants.getIcon(map));
setOpaque(GraphConstants.isOpaque(map));
setBorder(GraphConstants.getBorder(map));
setVerticalAlignment(GraphConstants.getVerticalAlignment(map));
setHorizontalAlignment(GraphConstants.getHorizontalAlignment(map));
bordercolor = GraphConstants.getBorderColor(map);
borderWidth = Math.max(1, Math.round(GraphConstants.getLineWidth(map)));
if (getBorder() == null && bordercolor != null)
setBorder(BorderFactory.createLineBorder(bordercolor, borderWidth));
Color gradientColor = GraphConstants.getGradientColor(map);
setGradientColor(gradientColor);
Color background = GraphConstants.getBackground(map);
setBackground((background != null) ? background : defaultBackground);
setFont(GraphConstants.getFont(map));
}
/**
* Draws a <code>String</code>. Its horizontal position
* <code>x</code> is given and its vertical position is centered on
* given <code>y</code>.
*
* @param g
* a <code>Graphics2D</code> to draw with
* @param label
* a <code>String</code> to draw
* @param x
* an offset to left edge of the bounding box of vertex
* @param y
* an offset to center of the string
* @param background
* the background color, if any, behind the text
* @param isLeftLabel
* whether or not this label is on the left-hand side of the vertex
*/
public static void drawPortLabel(Graphics g, String label, double x,
double y, Color background, boolean isLeftLabel) {
FontMetrics metrics = g.getFontMetrics();
int sw = metrics.stringWidth(label);
// If string is longer than allowed, concat it, replacing excess
// with "..."
int tries = 0;
if (sw > MAXLABELWIDTH) {
int dotLength = metrics.stringWidth("...");
while (sw > MAXLABELWIDTH && tries < 5) {
int characters = label.length();
double stringRatio = (double)(MAXLABELWIDTH-dotLength) / (double)sw;
int newStringLength = (int)((double)characters * stringRatio);
// Shorten string with each try
newStringLength -= tries;
if (newStringLength < characters) {
String newLabel = label.substring(0, newStringLength);
newLabel += "...";
int newStringWidth = metrics.stringWidth(newLabel);
if (newStringWidth < MAXLABELWIDTH) {
label = newLabel;
sw = newStringWidth;
}
}
tries++;
}
}
int sh = metrics.getHeight();
int offsetX = 0;
if (isLeftLabel) {
offsetX = PORTLABELSPACING;
} else {
offsetX = -sw - PORTLABELSPACING;
}
// Draw coloured rectangle is colour is set
if (background != null) {
Color oldColor = g.getColor();
g.setColor(background);
g.fillRect((int) x + offsetX - PORTLABELSPACING,
(int) (y - COLORHEIGHTBUFFER*2), sw + (PORTLABELSPACING * 2), sh + COLORHEIGHTBUFFER*2);
g.setColor(oldColor);
}
// int stringWidth = Math.min((int)x + offsetX, MAXLABELWIDTH);
g.drawString(label, (int) x + offsetX, (int) (y + sh / 2));
}
/**
* Paints port labels the view.
*
* @param g
* a <code>Graphics2D</code> to draw with
*/
public void paintPortLabels(Graphics g) {
if (ports != null && ports.length > 0) {
CellView parentView = ports[0].getParentView();
Rectangle2D bounds = GraphConstants.getBounds(parentView
.getAllAttributes());
// Get the bounds of the vertex and deduct twice the cell label
// height plus the vertical buffer distance from it.
double height = bounds.getHeight();
Color oldBackground = getBackground();
for (int i = 0; i < ports.length; i++) {
String labelValue = ports[i].getCell().toString();
if (labelValue == null) {
labelValue = new String();
}
Point2D portOffset = GraphConstants.getOffset(ports[i]
.getAllAttributes());
Color portBackground = GraphConstants.getBackground(ports[i]
.getAllAttributes());
double x = 0;
double y = 0;
if (portOffset != null) {
if (bounds != null) {
// By x position should be 0 or the width of the
// vertex
x = portOffset.getX() * bounds.getWidth()
/ GraphConstants.PERMILLE;
// The y position is the proportion of the vertex
// height available for port label. Remember a bit
// is reserved either end for the vertex label.
y = portOffset.getY() * height
/ GraphConstants.PERMILLE;
if (portOffset.getX() == 0) {
drawPortLabel(g, labelValue, x, y, portBackground, true);
} else if (portOffset.getX() == GraphConstants.PERMILLE) {
drawPortLabel(g, labelValue, x, y, portBackground, false);
}
}
}
}
setBackground(oldBackground);
}
}
public Dimension getPreferredSize() {
Dimension vertexPreferredSize = super.getPreferredSize();
if (ports != null && fontMetrics != null) {
int heightPerLabel = fontMetrics.getHeight()
+ MINIMUMVERTICALSPACING;
double left = 0, right = 0;
double maxLeft = 0, maxRight = 0;
for (int i = 0; i < ports.length; i++) {
String labelValue = ports[i].getCell().toString();
if (labelValue == null) {
labelValue = new String();
}
Point2D portOffset = GraphConstants.getOffset(ports[i]
.getAllAttributes());
int sw = fontMetrics.stringWidth(labelValue);
// Limit the size of labels to MAXLABELWIDTH
if (sw > MAXLABELWIDTH) sw = MAXLABELWIDTH;
if (portOffset != null) {
if (portOffset.getX() == 0) {
left += heightPerLabel;
maxLeft = Math.max(maxLeft, sw);
} else if (portOffset.getX() == GraphConstants.PERMILLE) {
right += heightPerLabel;
maxRight = Math.max(maxRight, sw);
}
}
}
int maxX = Math.max((int) vertexPreferredSize.getWidth(),
(int) (maxLeft + maxRight + MINIMUMHORIZONTALSPACING));
return new Dimension(maxX, (int) Math.max(left, right));
}
return vertexPreferredSize;
}
}
}