1 /*
2  * SimpleLabelPainter.java
3  *
4  * Copyright (C) 2006-2014 Andrew Rambaut
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 
21 package figtree.panel;
22 
23 import figtree.treeviewer.TreePane;
24 import figtree.treeviewer.painters.LabelPainter;
25 import figtree.treeviewer.decorators.Decorator;
26 import jebl.evolution.graphs.Node;
27 import jebl.evolution.taxa.Taxon;
28 import jebl.evolution.trees.RootedTree;
29 import jebl.evolution.trees.Tree;
30 import jebl.util.Attributable;
31 
32 import java.awt.*;
33 import java.awt.geom.Rectangle2D;
34 import java.util.*;
35 import java.util.List;
36 
37 /**
38  * A simple implementation of LabelPainter that can be used to display
39  * tip, node or branch labels. It can display, taxon names, branch lengths,
40  * node heights or other attributeNames of nodes.
41  *
42  * @author Andrew Rambaut
43  * @version $Id$
44  *
45  * $HeadURL$
46  *
47  * $LastChangedBy$
48  * $LastChangedDate$
49  * $LastChangedRevision$
50  */
51 public class SimpleLabelPainter extends LabelPainter<Node> {
52 
53     public static final String NAMES = "Names";
54     public static final String NODE_AGES = "Node ages";
55     public static final String BRANCH_LENGTHS = "Branch lengths";
56 
SimpleLabelPainter(PainterIntent intent)57     public SimpleLabelPainter(PainterIntent intent) {
58         super(intent);
59 
60         setupAttributes(null);
61 
62         if (this.displayAttribute == null) {
63             this.displayAttribute = attributes[0];
64         } else {
65             this.displayAttribute = "";
66         }
67 
68     }
69 
setupAttributes(Collection<? extends Tree> trees)70     public void setupAttributes(Collection<? extends Tree> trees) {
71 
72         List<String> attributeNames = new ArrayList<String>();
73         switch (getIntent()) {
74             case TIP: {
75                 attributeNames.add(NAMES);
76                 attributeNames.add(NODE_AGES);
77                 attributeNames.add(BRANCH_LENGTHS);
78                 break;
79             }
80             case NODE: {
81                 attributeNames.add(NODE_AGES);
82                 attributeNames.add(BRANCH_LENGTHS);
83                 break;
84             }
85             case BRANCH: {
86                 attributeNames.add(BRANCH_LENGTHS);
87                 attributeNames.add(NODE_AGES);
88                 break;
89             }
90         }
91 
92         if (trees != null) {
93             for (Tree tree : trees) {
94                 Set<String> nodeAttributes = new TreeSet<String>();
95                 if (getIntent() == PainterIntent.TIP) {
96                     for (Node node : tree.getExternalNodes()) {
97                         nodeAttributes.addAll(node.getAttributeNames());
98                     }
99                 } else if (getIntent() == PainterIntent.NODE) {
100                     for (Node node : tree.getInternalNodes()) {
101                         nodeAttributes.addAll(node.getAttributeNames());
102                     }
103                 } else {
104                     for (Node node : tree.getNodes()) {
105                         nodeAttributes.addAll(node.getAttributeNames());
106                     }
107                 }
108                 for (String attributeName : nodeAttributes) {
109                     if (!attributeName.startsWith("!")) {
110                         attributeNames.add(attributeName);
111                     }
112                 }
113             }
114         }
115 
116         this.attributes = new String[attributeNames.size()];
117         attributeNames.toArray(this.attributes);
118 
119         firePainterSettingsChanged();
120     }
121 
setTreePane(TreePane treePane)122     public void setTreePane(TreePane treePane) {
123         this.treePane = treePane;
124     }
125 
getBorderDecorator()126     public Decorator getBorderDecorator() {
127         return borderDecorator;
128     }
129 
setBorderDecorator(Decorator borderDecorator)130     public void setBorderDecorator(Decorator borderDecorator) {
131         this.borderDecorator = borderDecorator;
132     }
133 
getTextDecorator()134     public Decorator getTextDecorator() {
135         return textDecorator;
136     }
137 
setTextDecorator(Decorator textDecorator)138     public void setTextDecorator(Decorator textDecorator) {
139         this.textDecorator = textDecorator;
140     }
141 
getAttributableItems()142     public Set<Attributable> getAttributableItems() {
143         return null;
144     }
145 
getTree()146     public Tree getTree() {
147         return treePane.getTree();
148     }
149 
getLabel(Tree tree, Node node)150     protected String getLabel(Tree tree, Node node) {
151         if (displayAttribute.equalsIgnoreCase(NAMES)) {
152             Taxon taxon = tree.getTaxon(node);
153             if (taxon != null) {
154                 if (textDecorator != null) {
155                     textDecorator.setItem(taxon);
156                 }
157                 return taxon.getName();
158             } else {
159                 String name = (String)node.getAttribute("name");
160                 if (name != null) {
161                     return name;
162                 }
163                 return "unlabelled";
164             }
165         }
166 
167         if ( tree instanceof RootedTree) {
168             final RootedTree rtree = (RootedTree) tree;
169 
170             if (textDecorator != null) {
171                 textDecorator.setItem(node);
172             }
173 
174             if (displayAttribute.equalsIgnoreCase(NODE_AGES) ) {
175                 return getNumberFormat().format(rtree.getHeight(node));
176             } else if (displayAttribute.equalsIgnoreCase(BRANCH_LENGTHS) ) {
177                 return getNumberFormat().format(rtree.getLength(node));
178             }
179         }
180 
181         return formatValue(node.getAttribute(displayAttribute));
182     }
183 
formatValue(Object value)184     private String formatValue(Object value) {
185         if (value != null) {
186             if (value instanceof Double) {
187                 return getNumberFormat().format(value);
188             } else if (value instanceof Object[]) {
189                 Object[] values = (Object[])value;
190 
191                 if (values.length == 0) return null;
192                 if (values.length == 1) return formatValue(values[0]);
193 
194                 StringBuilder builder = new StringBuilder("[");
195                 builder.append(formatValue(values[0]));
196                 for (int i = 1; i < values.length; i++) {
197                     builder.append(",");
198                     builder.append(formatValue(values[i]));
199                 }
200                 builder.append("]");
201                 return builder.toString();
202             }
203             return value.toString();
204         }
205         return null;
206     }
207 
calibrate(Graphics2D g2, Node item)208     public Rectangle2D calibrate(Graphics2D g2, Node item) {
209         Tree tree = treePane.getTree();
210 
211         String label = getLabel(tree, item);
212 
213         final Font oldFont = g2.getFont();
214         if (textDecorator != null) {
215             g2.setFont(textDecorator.getFont(getFont()));
216         } else {
217             g2.setFont(getFont());
218         }
219 
220         FontMetrics fm = g2.getFontMetrics();
221         preferredHeight = fm.getHeight();
222         preferredWidth = 0;
223 
224         if (label != null) {
225             Rectangle2D rect = fm.getStringBounds(label, g2);
226             preferredWidth = rect.getWidth();
227         }
228 
229         yOffset = (float)fm.getAscent();
230 
231         g2.setFont(oldFont);
232 
233         return new Rectangle2D.Double(0.0, 0.0, preferredWidth, preferredHeight);
234     }
235 
getPreferredWidth()236     public double getPreferredWidth() {
237         return preferredWidth;
238     }
239 
getPreferredHeight()240     public double getPreferredHeight() {
241         return preferredHeight;
242     }
243 
getHeightBound()244     public double getHeightBound() {
245         return preferredHeight + yOffset;
246     }
247 
paint(Graphics2D g2, Node item, Justification justification, Rectangle2D bounds)248     public void paint(Graphics2D g2, Node item, Justification justification, Rectangle2D bounds) {
249         Tree tree = treePane.getTree();
250 
251         if (TreePane.DEBUG_OUTLINE) {
252             g2.setPaint(Color.red);
253             g2.draw(bounds);
254         }
255 
256         String label = getLabel(tree, item);
257 
258         Font oldFont = g2.getFont();
259 
260         Paint backgroundPaint = getBackground();
261         Paint borderPaint = getBorderPaint();
262         Stroke borderStroke = getBorderStroke();
263 
264         if (borderDecorator != null) {
265             backgroundPaint = borderDecorator.getPaint(backgroundPaint);
266             borderPaint = borderDecorator.getPaint(borderPaint);
267             borderStroke = borderDecorator.getStroke(borderStroke);
268         }
269 
270         if (backgroundPaint != null) {
271             g2.setPaint(backgroundPaint);
272             g2.fill(bounds);
273         }
274 
275         if (borderPaint != null && borderStroke != null) {
276             g2.setPaint(borderPaint);
277             g2.setStroke(borderStroke);
278             g2.draw(bounds);
279         }
280 
281         if (textDecorator != null) {
282             g2.setPaint(textDecorator.getPaint(getForeground()));
283             g2.setFont(textDecorator.getFont(getFont()));
284         } else {
285             g2.setPaint(getForeground());
286             g2.setFont(getFont());
287         }
288 
289         if (label != null) {
290 
291             Rectangle2D rect = null;
292             if (justification == Justification.CENTER || justification == Justification.RIGHT)
293                 rect = g2.getFontMetrics().getStringBounds(label, g2);
294 
295             float xOffset;
296             float y = yOffset + (float) bounds.getY();
297             switch (justification) {
298                 case CENTER:
299                     xOffset = (float)(-rect.getWidth()/2.0);
300                     y = yOffset + (float) rect.getY();
301 //xOffset = (float) (bounds.getX() + (bounds.getWidth() - rect.getWidth()) / 2.0);
302                     break;
303                 case FLUSH:
304                 case LEFT:
305                     xOffset = (float) bounds.getX();
306                     break;
307                 case RIGHT:
308                     xOffset = (float) (bounds.getX() + bounds.getWidth() - rect.getWidth());
309                     break;
310                 default:
311                     throw new IllegalArgumentException("Unrecognized alignment enum option");
312             }
313 
314             g2.drawString(label, xOffset, y);
315         }
316 
317         g2.setFont(oldFont);
318     }
319 
getAttributes()320     public String[] getAttributes() {
321         return attributes;
322     }
323 
getDisplayAttribute()324     public String getDisplayAttribute() {
325         return displayAttribute;
326     }
327 
setDisplayAttribute(String displayAttribute)328     public void setDisplayAttribute(String displayAttribute) {
329         this.displayAttribute = displayAttribute;
330         firePainterChanged();
331     }
332 
333     private double preferredWidth;
334     private double preferredHeight;
335     private float yOffset;
336 
337     protected String displayAttribute;
338     protected String[] attributes;
339 
340     protected TreePane treePane;
341 
342     private Decorator textDecorator = null;
343     private Decorator borderDecorator = null;
344 
345 }