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 }