001 /*--------------------------------------------------------------------------+
002 $Id: NodeTextRenderer.java 26931 2010-03-17 14:53:13Z besenreu $
003 | |
004 | Copyright 2005-2010 Technische Universitaet Muenchen |
005 | |
006 | Licensed under the Apache License, Version 2.0 (the "License"); |
007 | you may not use this file except in compliance with the License. |
008 | You may obtain a copy of the License at |
009 | |
010 | http://www.apache.org/licenses/LICENSE-2.0 |
011 | |
012 | Unless required by applicable law or agreed to in writing, software |
013 | distributed under the License is distributed on an "AS IS" BASIS, |
014 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
015 | See the License for the specific language governing permissions and |
016 | limitations under the License. |
017 +--------------------------------------------------------------------------*/
018 package edu.tum.cs.commons.treemap;
019
020 import java.awt.Color;
021 import java.awt.Graphics2D;
022 import java.awt.geom.Rectangle2D;
023 import java.util.regex.Pattern;
024
025
026 /**
027 * A simple renderer that draws tree map node texts into the tree map.
028 *
029 * @author juergens
030 * @author $Author: besenreu $
031 * @version $Rev: 26931 $
032 * @levd.rating GREEN Hash: 592D3A4F3823417DAADE575320937157
033 */
034 public class NodeTextRenderer implements ITreeMapRenderer {
035
036 /** Padding between a node's text label and its rectangle border */
037 private static final int TEXT_PADDING = 5;
038
039 /** Color in which text is drawn */
040 private final Color textColor;
041
042 /** Separation pattern used to isolate local name */
043 private final Pattern separationPattern;
044
045 /** Constructor */
046 public NodeTextRenderer(Color textColor, Pattern separationPattern) {
047 this.textColor = textColor;
048 this.separationPattern = separationPattern;
049 }
050
051 /** {@inheritDoc} */
052 public <T> void renderTreeMap(ITreeMapNode<T> node, Graphics2D graphics) {
053 if (node.getChildren().isEmpty()) {
054 Rectangle2D nodeArea = node.getLayoutRectangle();
055 if (enoughSpace(nodeArea)) {
056 drawText(node.getText(), nodeArea, graphics);
057 }
058 } else {
059 for (ITreeMapNode<T> child : node.getChildren()) {
060 renderTreeMap(child, graphics);
061 }
062 }
063 }
064
065 /** Determines if node area is large enough */
066 private boolean enoughSpace(Rectangle2D nodeArea) {
067 return nodeArea.getWidth() > 3 * TEXT_PADDING
068 && nodeArea.getHeight() > 3 * TEXT_PADDING;
069 }
070
071 /** Draws the node text */
072 private <T> void drawText(String text, Rectangle2D availableSpace,
073 Graphics2D graphics) {
074
075 // cut text to size
076 String fittedText = clipTextToWidth(text, availableSpace.getWidth(),
077 graphics);
078 if (fittedText.length() < text.length()) {
079 fittedText += "...";
080 }
081
082 // compute text position
083 int x = (int) availableSpace.getCenterX()
084 - (actualWidth(fittedText, graphics) / 2);
085 int y = (int) availableSpace.getCenterY()
086 + (actualHeight(fittedText, graphics) / 2);
087
088 // draw label
089 graphics.setColor(textColor);
090 graphics.drawString(fittedText, x, y);
091 }
092
093 /** Clips a string to a certain width */
094 private String clipTextToWidth(String text, double width,
095 Graphics2D graphics) {
096 double availableWidth = width - 2 * TEXT_PADDING;
097
098 // try to prune to last name part
099 if (separationPattern != null
100 && actualWidth(text, graphics) > availableWidth) {
101 String[] parts = separationPattern.split(text);
102 if (parts.length > 0) {
103 text = parts[parts.length - 1];
104 }
105 }
106
107 // reserve space for trailing "..."
108 if (actualWidth(text, graphics) > availableWidth) {
109 availableWidth -= actualWidth("...", graphics);
110 }
111
112 // clip until small enough
113 while (text.length() > 0
114 && actualWidth(text, graphics) > availableWidth) {
115 text = text.substring(0, text.length() - 1);
116 }
117 return text;
118 }
119
120 /** Determines the width a string requires in the current font */
121 private int actualWidth(String label, Graphics2D graphics) {
122 return (int) actualBounds(label, graphics).getWidth();
123 }
124
125 /** Determines the height a string requires in the current font */
126 private int actualHeight(String label, Graphics2D graphics) {
127 return (int) actualBounds(label, graphics).getHeight();
128 }
129
130 /** Computes the bounds of a string in the current font */
131 private Rectangle2D actualBounds(String label, Graphics2D graphics) {
132 return graphics.getFont().getStringBounds(label,
133 graphics.getFontRenderContext());
134 }
135
136 }