001: /*
002: * $Id: JGraphpadEdgeRenderer.java,v 1.5 2006/03/21 10:18:19 gaudenz Exp $
003: * Copyright (c) 2001-2005, Gaudenz Alder
004: *
005: * All rights reserved.
006: *
007: * This file is licensed under the JGraph software license, a copy of which
008: * will have been provided to you in the file LICENSE at the root of your
009: * installation directory. If you are unable to locate this file please
010: * contact JGraph sales for another copy.
011: */
012: package com.jgraph.pad.graph;
013:
014: import java.awt.Color;
015: import java.awt.Dimension;
016: import java.awt.Graphics;
017: import java.awt.Graphics2D;
018: import java.awt.geom.Point2D;
019: import java.util.Map;
020:
021: import javax.accessibility.AccessibleContext;
022: import javax.swing.BorderFactory;
023: import javax.swing.JLabel;
024: import javax.swing.JTextPane;
025: import javax.swing.SwingConstants;
026: import javax.swing.border.Border;
027: import javax.swing.text.SimpleAttributeSet;
028: import javax.swing.text.StyleConstants;
029: import javax.swing.text.StyledDocument;
030:
031: import org.jgraph.JGraph;
032: import org.jgraph.graph.CellView;
033: import org.jgraph.graph.EdgeRenderer;
034: import org.jgraph.graph.EdgeView;
035: import org.jgraph.graph.GraphConstants;
036:
037: public class JGraphpadEdgeRenderer extends EdgeRenderer {
038:
039: /**
040: * Holds the text pane to be used for rich text rendering.
041: */
042: public static JTextPane textPane = new JTextPane();
043:
044: /**
045: * An angular tolerance (actually a proportionality scalar) below which the
046: * edge label isn't rotated so that the font looks better
047: */
048: private static int angleTol = 10;
049:
050: /**
051: *
052: */
053: protected boolean isRichText = false;
054:
055: /**
056: *
057: */
058: private String text = ""; // "" rather than null, for BeanBox
059:
060: /**
061: *
062: */
063: private double x_buff, y_buff;
064:
065: /**
066: *
067: */
068: private int verticalAlignment = 0;
069:
070: /**
071: * Defines the default inset to render rich text.
072: */
073: public static int INSET = 4;
074:
075: /**
076: * Holds the user object of the current cell.
077: */
078: protected Object userObject = null;
079:
080: public JGraphpadEdgeRenderer() {
081: super ();
082: }
083:
084: /**
085: * Utility method to paint the rich text content for rich text values. This
086: * implementation simulates rich text vertical alignment by translating the
087: * graphics before painting the textPane.
088: *
089: * @param g
090: * The graphics to paint the rich text content to.
091: */
092: protected void paintRichText(Graphics g, int x, int y) {
093: g.translate(-x, -y);
094: textPane.paint(g);
095: g.translate(x, y);
096: }
097:
098: /**
099: * Paint the specified label for the current edgeview.
100: */
101: protected void paintLabel(Graphics g, String label, Point2D p,
102: boolean mainLabel) {
103: if (!mainLabel) {
104: super .paintLabel(g, label, p, mainLabel);
105: return;
106: }
107:
108: if (p != null && label != null && label.toString().length() > 0
109: && metrics != null) {
110: textPane.setSize(JGraphpadVertexRenderer.ZERO_DIMENSION);
111: Dimension d = textPane.getPreferredSize();
112: textPane.setSize(d.width, d.height - 14);
113: int sw = textPane.getWidth();
114: int sh = textPane.getHeight();
115: Graphics2D g2 = (Graphics2D) g;
116: boolean applyTransform = isLabelTransform(label);
117: double angle = 0;
118: int dx = -sw / 2;
119: int offset = isMoveBelowZero || applyTransform ? 0 : Math
120: .min(0, (int) (dx + p.getX()));
121:
122: g2.translate(p.getX() - offset, p.getY());
123: if (applyTransform) {
124: angle = getLabelAngle2(label);
125: g2.rotate(angle);
126: }
127:
128: int dy = (verticalAlignment == SwingConstants.TOP) ? sh
129: : ((verticalAlignment == SwingConstants.BOTTOM) ? 0
130: : sh / 2);
131: g.setColor(fontColor);
132: paintRichText(g, sw / 2, dy);
133: if (applyTransform) {
134: // Undo the transform
135: g2.rotate(-angle);
136: }
137: g2.translate(-p.getX() + offset, -p.getY());
138: }
139: }
140:
141: /**
142: * Calculates the angle at which graphics should be rotated to paint label
143: * along the edge. Before calling this method always check that transform
144: * should be applied using {@linkisLabelTransform}
145: *
146: * @return the value of the angle, 0 if the angle is zero or can't be
147: * calculated
148: */
149: private double getLabelAngle2(String label) {
150: Point2D p = getLabelPosition(view);
151: double angle = 0;
152: if (p != null && label != null && label.length() > 0) {
153: int sw = (int) textPane.getPreferredSize().getWidth();
154: // Note: For control points you may want to choose other
155: // points depending on the segment the label is in.
156: Point2D p1 = view.getPoint(0);
157: Point2D p2 = view.getPoint(view.getPointCount() - 1);
158: // Length of the edge
159: double length = Math.sqrt(x_buff + y_buff);
160: if (!(length <= Double.NaN || length < sw)) { // Label fits into
161: // edge's length
162:
163: // To calculate projections of edge
164: double cos = (p2.getX() - p1.getX()) / length;
165: double sin = (p2.getY() - p1.getY()) / length;
166:
167: // Determine angle
168: angle = Math.acos(cos);
169: if (sin < 0) { // Second half
170: angle = 2 * Math.PI - angle;
171: }
172: }
173: if (angle > Math.PI / 2 && angle <= Math.PI * 3 / 2) {
174: angle -= Math.PI;
175: }
176: }
177: return angle;
178: }
179:
180: /**
181: * Estimates whether the transform for label should be applied. With the
182: * transform, the label will be painted along the edge. To apply transform,
183: * rotate graphics by the angle returned from {@link #getLabelAngle}
184: *
185: * @return true, if transform can be applied, false otherwise
186: */
187: private boolean isLabelTransform(String label) {
188: if (!labelTransformEnabled) {
189: return false;
190: }
191: Point2D p = getLabelPosition(view);
192: if (p != null && label != null && label.length() > 0) {
193: int sw = (int) textPane.getPreferredSize().getWidth();
194: Point2D p1 = view.getPoint(0);
195: Point2D p2 = view.getPoint(view.getPointCount() - 1);
196: x_buff = p2.getX() - p1.getX();
197: y_buff = p2.getY() - p1.getY();
198: x_buff = x_buff * x_buff;
199: y_buff = y_buff * y_buff;
200: double length = Math.sqrt(x_buff + y_buff);
201: if (y_buff * angleTol < x_buff)
202: return false;
203: if (!(length <= Double.NaN || length < sw)) {
204: return true;
205: }
206: }
207: return false;
208: }
209:
210: /**
211: * Returns the label size of the specified view in the given graph.
212: */
213: public Dimension getLabelSize(EdgeView view, String label) {
214: Dimension d = super .getLabelSize(view, label);
215: if (isRichText) {
216: d = textPane.getPreferredSize();
217: d.height *= 2; // worst case verical alignment
218: }
219: return d;
220: }
221:
222: /**
223: * Defines the single line of text this component will display. If the value
224: * of text is null or empty string, nothing is displayed.
225: * <p>
226: * The default value of this property is null.
227: * <p>
228: * This is a JavaBeans bound property.
229: *
230: * @see #setVerticalTextPosition
231: * @see #setHorizontalTextPosition
232: * @see #setIcon
233: * @beaninfo preferred: true bound: true attribute: visualUpdate true
234: * description: Defines the single line of text this component
235: * will display.
236: */
237: public void setText(String text) {
238:
239: String oldAccessibleName = null;
240: if (accessibleContext != null) {
241: oldAccessibleName = accessibleContext.getAccessibleName();
242: }
243:
244: String oldValue = this .text;
245: this .text = text;
246: firePropertyChange("text", oldValue, text);
247:
248: if ((accessibleContext != null)
249: && (accessibleContext.getAccessibleName() != oldAccessibleName)) {
250: accessibleContext.firePropertyChange(
251: AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
252: oldAccessibleName, accessibleContext
253: .getAccessibleName());
254: }
255: if (text == null || oldValue == null || !text.equals(oldValue)) {
256: revalidate();
257: repaint();
258: }
259: }
260:
261: /**
262: * Extends the parent's method to configure the renderer for displaying the
263: * specified view.
264: *
265: * @param view
266: * The view to configure the renderer for.
267: */
268: public void installAttributes(CellView view) {
269: super .installAttributes(view);
270:
271: // workaround for missing graph reference at the first call before any
272: // edge will
273: // need to render rich text
274: if (graph == null)
275: return;
276: JGraph graph = (JGraph) this .graph.get();
277:
278: // Configures the rich text or component value
279: userObject = graph.getModel().getValue(view.getCell());
280: if (userObject instanceof JGraphpadBusinessObject) {
281: JGraphpadBusinessObject obj = (JGraphpadBusinessObject) userObject;
282: isRichText = obj.isRichText();
283: } else {
284: isRichText = false;
285: }
286: verticalAlignment = getVerticalAlignment(view
287: .getAllAttributes());
288: // Configures the rich text box for rendering the rich text
289: if (isRichText) {
290: StyledDocument document = (StyledDocument) textPane
291: .getDocument();
292: ((JGraphpadRichTextValue) ((JGraphpadBusinessObject) userObject)
293: .getValue()).insertInto(document);
294:
295: textPane.setBorder(GraphConstants.getBorder(view
296: .getAllAttributes()));
297: Color bordercolor = GraphConstants.getBorderColor(view
298: .getAllAttributes());
299: int borderWidth = Math.max(1, Math.round(GraphConstants
300: .getLineWidth(view.getAllAttributes())));
301: if (textPane.getBorder() == null && bordercolor != null) {
302: textPane.setBorder(BorderFactory.createLineBorder(
303: bordercolor, borderWidth));
304: }
305: Border insetBorder = BorderFactory.createEmptyBorder(INSET,
306: INSET, INSET, INSET);
307: textPane.setBorder(BorderFactory.createCompoundBorder(
308: textPane.getBorder(), insetBorder));
309: Color background = GraphConstants.getBackground(view
310: .getAllAttributes());
311: if (background != null) {
312: textPane.setBackground(background);
313: textPane.setOpaque(true);
314: } else {
315: textPane.setOpaque(false);
316: }
317:
318: // Uses the label's alignment and sets it on the text pane to work
319: // around the problem of the text pane alignments not being stored.
320: // Note: As a consequence a text pane can only have one alignment
321: // for all text it contains. It is not possible to align the
322: // paragraphs individually.
323: int align = GraphConstants.getHorizontalAlignment(view
324: .getAllAttributes());
325: SimpleAttributeSet sas = new SimpleAttributeSet();
326: align = (align == JLabel.CENTER) ? StyleConstants.ALIGN_CENTER
327: : (align == JLabel.RIGHT) ? StyleConstants.ALIGN_RIGHT
328: : StyleConstants.ALIGN_LEFT;
329: StyleConstants.setAlignment(sas, align);
330: document.setParagraphAttributes(0, document.getLength(),
331: sas, true);
332: }
333: }
334:
335: public int getVerticalAlignment(Map map) {
336: Integer intObj = (Integer) map
337: .get(GraphConstants.VERTICAL_ALIGNMENT);
338: if (intObj != null)
339: return intObj.intValue();
340: return JLabel.TOP;
341: }
342:
343: public static int getAngleTol() {
344: return angleTol;
345: }
346:
347: }
|