1 /*
2  * DefaultTreeViewer.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.treeviewer;
22 
23 import figtree.treeviewer.painters.*;
24 import jebl.evolution.graphs.Node;
25 import jebl.evolution.taxa.Taxon;
26 import jebl.evolution.trees.*;
27 import figtree.treeviewer.decorators.Decorator;
28 import figtree.treeviewer.treelayouts.TreeLayout;
29 import jam.panels.StatusProvider;
30 
31 import javax.swing.*;
32 import java.awt.*;
33 import java.awt.print.PageFormat;
34 import java.awt.print.PrinterException;
35 import java.util.*;
36 import java.util.List;
37 import java.util.regex.PatternSyntaxException;
38 
39 /**
40  * @author Andrew Rambaut
41  * @version $Id$
42  *
43  * $HeadURL$
44  *
45  * $LastChangedBy$
46  * $LastChangedDate$
47  * $LastChangedRevision$
48  */
49 public class DefaultTreeViewer extends TreeViewer {
50     private final static double ZOOM_SCALE = 0.02;
51     private final static double VERTICAL_EXPANSION_SCALE = 0.02;
52     private final static double ZOOM_POWER = 1.2;
53 
DefaultTreeViewer()54     public DefaultTreeViewer() {
55         this(null);
56     }
57 
58     /**
59      * Creates new TreeViewer
60      */
DefaultTreeViewer(JFrame frame)61     public DefaultTreeViewer(JFrame frame) {
62         this.frame = frame;
63 
64         setLayout(new BorderLayout());
65 
66         this.treePane = new TreePane();
67         treePane.setAutoscrolls(true); //enable synthetic drag events
68 
69         treePane.addTreePaneListener(new TreePaneListener() {
70             public void treePaneSettingsChanged() {
71                 fireTreeSettingsChanged();
72             }
73         });
74 
75         JScrollPane scrollPane = new JScrollPane(treePane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
76         scrollPane.setMinimumSize(new Dimension(150, 150));
77 
78         scrollPane.setBorder(null);
79         viewport = scrollPane.getViewport();
80 
81         add(scrollPane, BorderLayout.CENTER);
82 
83         // This overrides MouseListener and MouseMotionListener to allow selection in the TreePane -
84         // It installs itself within the constructor.
85         treePaneSelector = new TreePaneSelector(treePane);
86         treePaneRollOver = new TreePaneRollOver(treePane);
87 
88         setFocusable(true);
89 
90     }
91 
setTree(Tree tree)92     public void setTree(Tree tree) {
93         trees.clear();
94         addTree(tree);
95         showTree(0);
96     }
97 
setTrees(Collection<? extends Tree> trees)98     public void setTrees(Collection<? extends Tree> trees) {
99         this.trees.clear();
100         for (Tree tree : trees) {
101             addTree(tree);
102         }
103         showTree(0);
104     }
105 
addTree(Tree tree)106     public void addTree(Tree tree) {
107         this.trees.add(tree);
108 
109 //        if (treePane.getTipLabelPainter() != null) {
110 //            treePane.getTipLabelPainter().setupAttributes(trees);
111 //        }
112 //
113 //        if (treePane.getBranchLabelPainter() != null) {
114 //            treePane.getBranchLabelPainter().setupAttributes(trees);
115 //        }
116 //
117 //        if (treePane.getNodeLabelPainter() != null) {
118 //            treePane.getNodeLabelPainter().setupAttributes(trees);
119 //        }
120 //
121 //        if (treePane.getNodeBarPainter() != null) {
122 //            treePane.getNodeBarPainter().setupAttributes(trees);
123 //        }
124 //
125 //        if (treePane.getLegendPainter() != null) {
126 //            treePane.getLegendPainter().setupAttributes(trees);
127 //        }
128     }
129 
addTrees(Collection<? extends Tree> trees)130     public void addTrees(Collection<? extends Tree> trees) {
131         int count = getTreeCount();
132         for (Tree tree : trees) {
133             addTree(tree);
134         }
135         showTree(count);
136     }
137 
getTrees()138     public List<Tree> getTrees() {
139         return trees;
140     }
141 
getCurrentTree()142     public Tree getCurrentTree() {
143 //        return trees.get(currentTreeIndex);
144         return treePane.getTree();
145     }
146 
getTreesAsViewed()147     public List<Tree> getTreesAsViewed() {
148         List<Tree> treeAsDisplayed = new ArrayList<Tree>();
149         for (Tree originalTree : trees) {
150             treeAsDisplayed.add(treePane.constructTransformedTree((RootedTree)originalTree));
151         }
152         return treeAsDisplayed;
153     }
154 
getSelectedSubtree()155     public RootedTree getSelectedSubtree() {
156         return treePane.getSelectedSubtree();
157     }
158 
getCurrentTreeIndex()159     public int getCurrentTreeIndex() {
160         return currentTreeIndex;
161     }
162 
getTreeCount()163     public int getTreeCount() {
164         if (trees == null) return 0;
165         return trees.size();
166     }
167 
getStatusProvider()168     public StatusProvider getStatusProvider() {
169         return treePaneRollOver;
170     }
171 
showTree(int index)172     public void showTree(int index) {
173         if (isRootingOn() && getRootingType() == TreePane.RootingType.USER_ROOTING) {
174             JOptionPane.showMessageDialog(frame, "Cannot switch trees when user rooting option is on.\n" +
175                     "Turn this option off to switch trees",
176                     "Unable to switch trees",
177                     JOptionPane.ERROR_MESSAGE);
178             return;
179         }
180 
181         Tree tree = trees.get(index);
182         if (tree instanceof RootedTree) {
183             treePane.setTree((RootedTree)tree);
184         } else {
185             treePane.setTree(Utils.rootTheTree(tree));
186         }
187 
188         currentTreeIndex = index;
189         fireTreeChanged();
190     }
191 
showNextTree()192     public void showNextTree() {
193         if (currentTreeIndex < trees.size() - 1) {
194             showTree(currentTreeIndex + 1);
195         }
196     }
197 
showPreviousTree()198     public void showPreviousTree() {
199         if (currentTreeIndex > 0) {
200             showTree(currentTreeIndex - 1);
201         }
202     }
203 
setTreeLayout(TreeLayout treeLayout)204     public void setTreeLayout(TreeLayout treeLayout) {
205         treePane.setTreeLayout(treeLayout);
206         fireTreeSettingsChanged();
207     }
208 
209     private boolean zoomPending = false;
210     private double zoom = 0.0;
211     private double verticalExpansion = 0.0;
212 
setZoom(double zoom)213     public void setZoom(double zoom) {
214         double n = Math.max(treePane.getTree().getTaxa().size(), 50);
215         this.zoom = Math.pow(zoom * n * ZOOM_SCALE, ZOOM_POWER);
216 //        this.zoom = zoom * MAX_ZOOM;
217         refreshZoom();
218     }
219 
setVerticalExpansion(double verticalExpansion)220     public void setVerticalExpansion(double verticalExpansion) {
221         double n = Math.max(treePane.getTree().getTaxa().size(), 50);
222         this.verticalExpansion = Math.pow(verticalExpansion * n * VERTICAL_EXPANSION_SCALE, ZOOM_POWER);
223 //        this.verticalExpansion = verticalExpansion * MAX_VERTICAL_EXPANSION;
224         refreshZoom();
225     }
226 
verticalExpansionAllowed()227     public boolean verticalExpansionAllowed() {
228         return !treePane.maintainAspectRatio();
229     }
230 
setTimeScale(TimeScale timeScale)231     public void setTimeScale(TimeScale timeScale) {
232         treePane.setTimeScale(timeScale);
233     }
234 
refreshZoom()235     private void refreshZoom() {
236         setZoom(zoom, zoom + verticalExpansion);
237     }
238 
setZoom(double xZoom, double yZoom)239     private void setZoom(double xZoom, double yZoom) {
240 
241         Dimension viewportSize = viewport.getViewSize();
242         Point position = viewport.getViewPosition();
243 
244         Dimension extentSize = viewport.getExtentSize();
245         double w = extentSize.getWidth() * (1.0 + xZoom);
246         double h = extentSize.getHeight() * (1.0 + yZoom);
247 
248         Dimension newSize = new Dimension((int) w, (int) h);
249         treePane.setPreferredSize(newSize);
250 
251         double cx = position.getX() + (0.5 * extentSize.getWidth());
252         double cy = position.getY() + (0.5 * extentSize.getHeight());
253 
254         double rx = ((double) newSize.getWidth()) / viewportSize.getWidth();
255         double ry = ((double) newSize.getHeight()) / viewportSize.getHeight();
256 
257         double px = (cx * rx) - (extentSize.getWidth() / 2.0);
258         double py = (cy * ry) - (extentSize.getHeight() / 2.0);
259 
260         Point newPosition = new Point((int) px, (int) py);
261         viewport.setViewPosition(newPosition);
262         treePane.revalidate();
263     }
264 
hasSelection()265     public boolean hasSelection() {
266         return treePane.hasSelection();
267     }
268 
getSelectedNodes()269     public Set<Node> getSelectedNodes() {
270         return treePane.getSelectedNodes();
271     }
272 
getSelectedTips()273     public Set<Node> getSelectedTips() {
274         return treePane.getSelectedTips();
275     }
276 
getSelectedTaxa()277     public Set<Taxon> getSelectedTaxa() {
278         return treePane.getSelectedTaxa();
279     }
280 
281     /**
282      * Select taxa with a search string matching the currently displayed attribute
283      * @param searchType
284      * @param searchString
285      * @param caseSensitive
286      */
selectTaxa(TextSearchType searchType, String searchString, boolean caseSensitive)287     public void selectTaxa(TextSearchType searchType, String searchString, boolean caseSensitive) {
288         String attributeName = "!name";
289         LabelPainter lp = treePane.getTipLabelPainter();
290         if (lp != null && lp.isVisible() && lp.getDisplayAttribute() != null) {
291             attributeName = lp.getDisplayAttribute();
292         }
293 
294         selectTaxa(attributeName, searchType, searchString, caseSensitive);
295 
296         scrollToSelectedTips();
297     }
298 
selectTaxa(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive)299     public void selectTaxa(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive) {
300         if (treePane.getTree() == null) {
301             return;
302         }
303 
304         treePane.clearSelection();
305 
306         String query = searchString;
307 
308         if (searchType != TextSearchType.REG_EX) {
309             query = (caseSensitive ? searchString : searchString.toUpperCase());
310             query = query.trim();
311         }
312 
313         Tree tree = treePane.getTree();
314 
315         for (Node node : tree.getExternalNodes()) {
316             Taxon taxon = tree.getTaxon(node);
317 
318             if (attributeName == null) {
319                 Object target = taxon.getName();
320                 if (matchesItem(searchType, target, query, caseSensitive)) {
321                     treePane.addSelectedTip(node);
322                     break;
323                 }
324                 for (String name : taxon.getAttributeNames()) {
325                     target = taxon.getAttribute(name);
326                     if (matchesItem(searchType, target, query, caseSensitive)) {
327                         treePane.addSelectedTip(node);
328                         break;
329                     }
330                 }
331             } else {
332                 Object target;
333                 if (attributeName.equals("!name")) {
334                     target = taxon.getName();
335                 } else {
336                     target = taxon.getAttribute(attributeName);
337                     if (target == null) {
338                         // if we can't find the attribute on the taxon, try the node
339                         target = node.getAttribute(attributeName);
340                     }
341                 }
342                 if (matchesItem(searchType, target, query, caseSensitive)) {
343                     treePane.addSelectedTip(node);
344                 }
345             }
346         }
347     }
348 
selectNodes(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive)349     public void selectNodes(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive) {
350         treePane.clearSelection();
351 
352         String query = searchString;
353 
354         if (searchType != TextSearchType.REG_EX) {
355             query = (caseSensitive ? searchString : searchString.toUpperCase());
356             query = query.trim();
357         }
358 
359         Tree tree = treePane.getTree();
360 
361         for (Node node : tree.getNodes()) {
362             if (attributeName == null) {
363                 for (String name : node.getAttributeNames()) {
364                     Object target = node.getAttribute(name);
365                     if (matchesItem(searchType, target, query, caseSensitive)) {
366                         treePane.addSelectedNode(node);
367                         break;
368                     }
369                 }
370             } else {
371                 Object target = node.getAttribute(attributeName);
372                 if (matchesItem(searchType, target, query, caseSensitive)) {
373                     treePane.addSelectedNode(node);
374                 }
375             }
376         }
377     }
378 
matchesItem(TextSearchType searchType, Object object, String query, boolean caseSensitive)379     private boolean matchesItem(TextSearchType searchType, Object object, String query, boolean caseSensitive) {
380 
381         if (object != null) {
382             String target = (caseSensitive ? object.toString() : object.toString().toUpperCase());
383             switch (searchType) {
384                 case CONTAINS:
385                     if (target.contains(query)) {
386                         return true;
387                     }
388                     break;
389                 case STARTS_WITH:
390                     if (target.startsWith(query)) {
391                         return true;
392                     }
393                     break;
394                 case ENDS_WITH:
395                     if (target.endsWith(query)) {
396                         return true;
397                     }
398                     break;
399                 case MATCHES:
400                     if (target.equals(query)) {
401                         return true;
402                     }
403                     break;
404                 case REG_EX:
405                     try {
406                         if (target.matches(query)) {
407                             return true;
408                         }
409                     } catch (PatternSyntaxException pse) {
410                         // ignore
411                     }
412                     break;
413             }
414         }
415         return false;
416     }
417 
selectTaxa(String attributeName, NumberSearchType searchType, Number searchValue)418     public void selectTaxa(String attributeName, NumberSearchType searchType, Number searchValue) {
419         treePane.clearSelection();
420 
421         RootedTree tree = treePane.getTree();
422 
423         for (Node node : tree.getExternalNodes()) {
424             Object value = null;
425             if (attributeName.equals("!length")) {
426                 value = tree.getLength(node);
427             } else if (attributeName.equals("!height")) {
428                 value = tree.getHeight(node);
429             } else {
430                 Taxon taxon = tree.getTaxon(node);
431                 value = taxon.getAttribute(attributeName);
432             }
433             if (matchesItem(value, searchType, searchValue)) {
434                 treePane.addSelectedTip(node);
435             }
436         }
437     }
438 
selectNodes(String attributeName, NumberSearchType searchType, Number searchValue)439     public void selectNodes(String attributeName, NumberSearchType searchType, Number searchValue) {
440         treePane.clearSelection();
441 
442         RootedTree tree = treePane.getTree();
443 
444         for (Node node : tree.getNodes()) {
445             Object value = null;
446             if (attributeName.equals("!length")) {
447                 value = tree.getLength(node);
448             } else if (attributeName.equals("!height")) {
449                 value = tree.getHeight(node);
450             } else {
451                 value = node.getAttribute(attributeName);
452             }
453             if (matchesItem(value, searchType, searchValue)) {
454                 treePane.addSelectedNode(node);
455             }
456         }
457     }
458 
selectTaxa(Collection<String> taxonNames)459     public void selectTaxa(Collection<String> taxonNames) {
460         treePane.clearSelection();
461 
462         RootedTree tree = treePane.getTree();
463 
464         for (Node node : tree.getExternalNodes()) {
465             Object value = null;
466             if (taxonNames.contains(tree.getTaxon(node).getName())) {
467                 treePane.addSelectedTip(node);
468             }
469         }
470     }
471 
matchesItem(Object item, NumberSearchType searchType, Number searchValue)472     private boolean matchesItem(Object item, NumberSearchType searchType, Number searchValue) {
473         if (item != null && item instanceof Number) {
474 
475             Number value = (Number)item;
476 
477             switch (searchType) {
478                 case EQUALS:
479                     if (value.equals(searchValue)) {
480                         return true;
481                     }
482                     break;
483                 case EQUALS_OR_GREATER_THAN:
484                     if (value.doubleValue() >= searchValue.doubleValue()) {
485                         return true;
486                     }
487                     break;
488                 case EQUALS_OR_LESS_THAN:
489                     if (value.doubleValue() <= searchValue.doubleValue()) {
490                         return true;
491                     }
492                     break;
493                 case GREATER_THAN:
494                     if (value.doubleValue() > searchValue.doubleValue()) {
495                         return true;
496                     }
497                     break;
498                 case LESS_THAN:
499                     if (value.doubleValue() < searchValue.doubleValue()) {
500                         return true;
501                     }
502                     break;
503                 case NOT_EQUALS:
504                     if (!searchValue.equals(value)) {
505                         return true;
506                     }
507                     break;
508             }
509         }
510         return false;
511     }
512 
scrollToSelectedTips()513     public void scrollToSelectedTips() {
514         Set<Node> selectedTips = treePane.getSelectedTips();
515         if (selectedTips.size() > 0) {
516             Point point = treePane.getLocationOfTip(selectedTips.iterator().next());
517             treePane.scrollPointToVisible(point);
518         }
519     }
520 
cartoonSelectedNodes()521     public void cartoonSelectedNodes() {
522         treePane.cartoonSelectedNodes();
523         fireTreeSettingsChanged();
524     }
525 
collapseSelectedNodes()526     public void collapseSelectedNodes() {
527         treePane.collapseSelectedNodes();
528         fireTreeSettingsChanged();
529     }
530 
clearCollapsedNodes()531     public void clearCollapsedNodes() {
532         treePane.clearCollapsedNodes();
533         fireTreeSettingsChanged();
534     }
535 
hilightSelectedNodes(Color color)536     public void hilightSelectedNodes(Color color) {
537         treePane.hilightSelectedNodes(color);
538         fireTreeSettingsChanged();
539     }
540 
clearHilighting()541     public void clearHilighting() {
542         treePane.clearHilightedNodes();
543         fireTreeSettingsChanged();
544     }
545 
rerootOnSelectedBranch()546     public void rerootOnSelectedBranch() {
547         treePane.rerootOnSelectedBranch();
548         fireTreeSettingsChanged();
549     }
550 
clearRooting()551     public void clearRooting() {
552         treePane.clearRooting();
553         fireTreeSettingsChanged();
554     }
555 
rotateSelectedNode()556     public void rotateSelectedNode() {
557         treePane.rotateSelectedNode();
558         fireTreeSettingsChanged();
559     }
560 
clearRotations()561     public void clearRotations() {
562         treePane.clearSelectedNodeRotations();
563         fireTreeSettingsChanged();
564     }
565 
annotateSelectedNodes(String name, Object value)566     public void annotateSelectedNodes(String name, Object value) {
567         treePane.annotateSelectedNodes(name, value);
568         fireTreeSettingsChanged();
569     }
570 
annotateSelectedTips(String name, Object value)571     public void annotateSelectedTips(String name, Object value) {
572         treePane.annotateSelectedTips(name, value);
573         fireTreeSettingsChanged();
574     }
575 
clearAnnotation(String name)576     public void clearAnnotation(String name) {
577         treePane.clearSelectedNodeAnnotation(name);
578         treePane.clearSelectedTipAnnotation(name);
579         fireTreeSettingsChanged();
580     }
581 
clearColouring()582     public void clearColouring() {
583         treePane.clearSelectedNodeAnnotation("!color");
584         treePane.clearSelectedTipAnnotation("!color");
585         fireTreeSettingsChanged();
586     }
587 
selectAll()588     public void selectAll() {
589         if (treePaneSelector.getSelectionMode() == TreePaneSelector.SelectionMode.TAXA) {
590             treePane.selectAllTaxa();
591         } else {
592             treePane.selectAllNodes();
593         }
594     }
595 
clearSelectedTaxa()596     public void clearSelectedTaxa() {
597         treePane.clearSelection();
598     }
599 
addTreeSelectionListener(TreeSelectionListener treeSelectionListener)600     public void addTreeSelectionListener(TreeSelectionListener treeSelectionListener) {
601         treePane.addTreeSelectionListener(treeSelectionListener);
602     }
603 
removeTreeSelectionListener(TreeSelectionListener treeSelectionListener)604     public void removeTreeSelectionListener(TreeSelectionListener treeSelectionListener) {
605         treePane.removeTreeSelectionListener(treeSelectionListener);
606     }
607 
getSelectionMode()608     public TreePaneSelector.SelectionMode getSelectionMode() {
609         return treePaneSelector.getSelectionMode();
610     }
611 
612 
setSelectionMode(TreePaneSelector.SelectionMode selectionMode)613     public void setSelectionMode(TreePaneSelector.SelectionMode selectionMode) {
614         TreePaneSelector.SelectionMode oldSelectionMode = treePaneSelector.getSelectionMode();
615 
616         if (selectionMode == oldSelectionMode) {
617             return;
618         }
619 
620         if (oldSelectionMode == TreePaneSelector.SelectionMode.TAXA) {
621             treePane.selectNodesFromSelectedTips();
622         } else if (selectionMode == TreePaneSelector.SelectionMode.TAXA) {
623             treePane.selectTipsFromSelectedNodes();
624         } else if (selectionMode == TreePaneSelector.SelectionMode.CLADE) {
625             treePane.selectCladesFromSelectedNodes();
626         }
627         treePaneSelector.setSelectionMode(selectionMode);
628     }
629 
setDragMode(TreePaneSelector.DragMode dragMode)630     public void setDragMode(TreePaneSelector.DragMode dragMode) {
631         treePaneSelector.setDragMode(dragMode);
632     }
633 
634     // A load of delegated method calls through to treePane (which is now hidden outside the package).
setTipLabelPainter(LabelPainter<Node> tipLabelPainter)635     public void setTipLabelPainter(LabelPainter<Node> tipLabelPainter) {
636         treePane.setTipLabelPainter(tipLabelPainter);
637 //        tipLabelPainter.setupAttributes(trees);
638 
639     }
640 
setNodeLabelPainter(LabelPainter<Node> nodeLabelPainter)641     public void setNodeLabelPainter(LabelPainter<Node> nodeLabelPainter) {
642         treePane.setNodeLabelPainter(nodeLabelPainter);
643 //        nodeLabelPainter.setupAttributes(trees);
644     }
645 
setNodeBarPainter(NodeBarPainter nodeBarPainter)646     public void setNodeBarPainter(NodeBarPainter nodeBarPainter) {
647         treePane.setNodeBarPainter(nodeBarPainter);
648 //        nodeBarPainter.setupAttributes(trees);
649     }
650 
setTipShapePainter(NodeShapePainter tipShapePainter)651     public void setTipShapePainter(NodeShapePainter tipShapePainter) {
652         treePane.setTipShapePainter(tipShapePainter);
653     }
654 
setNodeShapePainter(NodeShapePainter nodeShapePainter)655     public void setNodeShapePainter(NodeShapePainter nodeShapePainter) {
656         treePane.setNodeShapePainter(nodeShapePainter);
657     }
658 
setBranchLabelPainter(LabelPainter<Node> branchLabelPainter)659     public void setBranchLabelPainter(LabelPainter<Node> branchLabelPainter) {
660         treePane.setBranchLabelPainter(branchLabelPainter);
661 //        branchLabelPainter.setupAttributes(trees);
662     }
663 
addScalePainter(ScalePainter scalePainter)664     public void addScalePainter(ScalePainter scalePainter) {
665         treePane.addScalePainter(scalePainter);
666     }
667 
removeScalePainter(ScalePainter scalePainter)668     public void removeScalePainter(ScalePainter scalePainter) {
669         treePane.removeScalePainter(scalePainter);
670     }
671 
setScaleGridPainter(ScaleGridPainter scaleGridPainter)672     public void setScaleGridPainter(ScaleGridPainter scaleGridPainter) {
673         treePane.setScaleGridPainter(scaleGridPainter);
674     }
675 
setLegendPainter(LegendPainter legendPainter)676     public void setLegendPainter(LegendPainter legendPainter) {
677         treePane.setLegendPainter(legendPainter);
678 //        legendPainter.setupAttributes(trees);
679     }
680 
setBranchDecorator(Decorator branchDecorator, boolean isGradient)681     public void setBranchDecorator(Decorator branchDecorator, boolean isGradient) {
682         treePane.setBranchDecorator(branchDecorator, isGradient);
683     }
684 
setBranchColouringDecorator(String branchColouringAttribute, Decorator branchColouringDecorator)685     public void setBranchColouringDecorator(String branchColouringAttribute, Decorator branchColouringDecorator) {
686         treePane.setBranchColouringDecorator(branchColouringAttribute, branchColouringDecorator);
687     }
688 
setNodeBackgroundDecorator(Decorator nodeBackgroundDecorator)689     public void setNodeBackgroundDecorator(Decorator nodeBackgroundDecorator) {
690         treePane.setNodeBackgroundDecorator(nodeBackgroundDecorator);
691     }
692 
setHilightingGradient(boolean hilightingGradient)693     public void setHilightingGradient(boolean hilightingGradient) {
694         treePane.setHilightingGradient(hilightingGradient);
695     }
696 
setSelectionColor(Color selectionColor)697     public void setSelectionColor(Color selectionColor) {
698         treePane.setSelectionColor(selectionColor);
699     }
700 
getSelectionPaint()701     public Paint getSelectionPaint() {
702         return treePane.getSelectionPaint();
703     }
704 
setBranchStroke(BasicStroke branchStroke)705     public void setBranchStroke(BasicStroke branchStroke) {
706         treePane.setBranchStroke(branchStroke);
707     }
708 
isTransformBranchesOn()709     public boolean isTransformBranchesOn() {
710         return treePane.isTransformBranchesOn();
711     }
712 
getBranchTransform()713     public TransformedRootedTree.Transform getBranchTransform() {
714         return treePane.getBranchTransform();
715     }
716 
setTransformBranchesOn(boolean transformBranchesOn)717     public void setTransformBranchesOn(boolean transformBranchesOn) {
718         treePane.setTransformBranchesOn(transformBranchesOn);
719     }
720 
setBranchTransform(TransformedRootedTree.Transform transform)721     public void setBranchTransform(TransformedRootedTree.Transform transform) {
722         treePane.setBranchTransform(transform);
723     }
724 
isOrderBranchesOn()725     public boolean isOrderBranchesOn() {
726         return treePane.isOrderBranchesOn();
727     }
728 
getBranchOrdering()729     public SortedRootedTree.BranchOrdering getBranchOrdering() {
730         return treePane.getBranchOrdering();
731     }
732 
setOrderBranchesOn(boolean orderBranchesOn)733     public void setOrderBranchesOn(boolean orderBranchesOn) {
734         treePane.setOrderBranchesOn(orderBranchesOn);
735     }
736 
setBranchOrdering(SortedRootedTree.BranchOrdering branchOrdering)737     public void setBranchOrdering(SortedRootedTree.BranchOrdering branchOrdering) {
738         treePane.setBranchOrdering(branchOrdering);
739     }
740 
isRootingOn()741     public boolean isRootingOn() {
742         return treePane.isRootingOn();
743     }
744 
getRootingType()745     public TreePane.RootingType getRootingType() {
746         return treePane.getRootingType();
747     }
748 
setRootingOn(boolean rootingOn)749     public void setRootingOn(boolean rootingOn) {
750         treePane.setRootingOn(rootingOn);
751     }
752 
setRootingType(TreePane.RootingType rootingType)753     public void setRootingType(TreePane.RootingType rootingType) {
754         treePane.setRootingType(rootingType);
755     }
756 
setToolMode(TreePaneSelector.ToolMode toolMode)757     public void setToolMode(TreePaneSelector.ToolMode toolMode) {
758         treePaneSelector.setToolMode(toolMode);
759     }
760 
getContentPane()761     public JComponent getContentPane() {
762         return treePane;
763     }
764 
paint(Graphics g)765     public void paint(Graphics g) {
766         if( zoomPending  ) {
767             refreshZoom();
768             zoomPending = false;
769         }
770         super.paint(g);
771     }
772 
print(Graphics g, PageFormat pageFormat, int pageIndex)773     public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
774         return treePane.print(g, pageFormat, pageIndex);
775     }
776 
addTreeViewerListener(TreeViewerListener listener)777     public void addTreeViewerListener(TreeViewerListener listener) {
778         listeners.add(listener);
779     }
780 
removeTreeViewerListener(TreeViewerListener listener)781     public void removeTreeViewerListener(TreeViewerListener listener) {
782         listeners.remove(listener);
783     }
784 
fireTreeChanged()785     public void fireTreeChanged() {
786         for (TreeViewerListener listener : listeners) {
787             listener.treeChanged();
788         }
789     }
790 
fireTreeSettingsChanged()791     public void fireTreeSettingsChanged() {
792         for (TreeViewerListener listener : listeners) {
793             listener.treeSettingsChanged();
794         }
795     }
796 
797     private java.util.List<TreeViewerListener> listeners = new ArrayList<TreeViewerListener>();
798 
799     private List<Tree> trees = new ArrayList<Tree>();
800     private int currentTreeIndex = 0;
801 
802     protected TreePane treePane;
803     protected TreePaneSelector treePaneSelector;
804     protected TreePaneRollOver treePaneRollOver;
805 
806     protected JViewport viewport;
807 
808     private final JFrame frame;
809 }