1 /*
2  * MultiPaneTreeViewer.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.trees.*;
25 import jebl.evolution.graphs.Node;
26 import figtree.treeviewer.treelayouts.TreeLayout;
27 import figtree.treeviewer.decorators.Decorator;
28 
29 import javax.swing.*;
30 import java.awt.*;
31 import java.awt.print.*;
32 import java.util.*;
33 
34 import jam.panels.StatusProvider;
35 
36 /**
37  * @author Andrew Rambaut
38  * @version $Id$
39  *
40  * $HeadURL$
41  *
42  * $LastChangedBy$
43  * $LastChangedDate$
44  * $LastChangedRevision$
45  */
46 public class MultiPaneTreeViewer extends TreeViewer {
47 
48 	private final static double MAX_ZOOM = 20;
49 	private final static double MAX_VERTICAL_EXPANSION = 20;
50 
51 	/**
52 	 * Creates new TreeViewer
53 	 */
MultiPaneTreeViewer()54 	public MultiPaneTreeViewer() {
55 		treePanes.add(new TreePane());
56 
57 		setLayout(new BorderLayout());
58 
59 		treePanePanel = new MultiPaneTreePanel();
60 		treePanePanel.setLayout(new BoxLayout(treePanePanel, BoxLayout.PAGE_AXIS));
61 
62 		JScrollPane scrollPane = new JScrollPane(treePanePanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
63 		scrollPane.setMinimumSize(new Dimension(150, 150));
64 
65 		scrollPane.setBorder(null);
66 		viewport = scrollPane.getViewport();
67 
68 		add(scrollPane, BorderLayout.CENTER);
69 
70 	}
71 
setTree(Tree tree)72 	public void setTree(Tree tree) {
73 		trees.clear();
74 		addTree(tree);
75 		showTree(0);
76 	}
77 
setTrees(Collection<? extends Tree> trees)78 	public void setTrees(Collection<? extends Tree> trees) {
79 		this.trees.clear();
80 		for (Tree tree : trees) {
81 			addTree(tree);
82 		}
83 		showTree(0);
84 	}
85 
addTree(Tree tree)86 	protected void addTree(Tree tree) {
87 		this.trees.add(tree);
88 		showTree(trees.size() - 1);
89     }
90 
addTrees(Collection<? extends Tree> trees)91 	public void addTrees(Collection<? extends Tree> trees) {
92 		int count = getTreeCount();
93 		for (Tree tree : trees) {
94 			addTree(tree);
95 		}
96 		showTree(count);
97 	}
98 
getTree()99 	public Tree getTree() {
100 		return trees.get(0);
101 	}
102 
getTrees()103 	public java.util.List<Tree> getTrees() {
104 		return trees;
105 	}
106 
getTreesPerPage()107 	public int getTreesPerPage() {
108 		return treesPerPage;
109 	}
110 
setTreesPerPage(int treesPerPage)111 	public void setTreesPerPage(int treesPerPage) {
112 		this.treesPerPage = treesPerPage;
113 		if (treePanes.size() < treesPerPage) {
114 			while (treePanes.size() < treesPerPage) {
115 				treePanes.add(new TreePane());
116 			}
117 		} else if (treePanes.size() > treesPerPage) {
118 			while (treePanes.size() > treesPerPage) {
119 				treePanes.remove(treePanes.size() - 1);
120 			}
121 		}
122 		showTree(currentTreeIndex);
123 	}
124 
setupTreePane(TreePane treePane)125 	private void setupTreePane(TreePane treePane) {
126 		treePane.setAutoscrolls(true); //enable synthetic drag events
127 
128 		// This overrides MouseListener and MouseMotionListener to allow selection in the TreePane -
129 		// It installs itself within the constructor.
130 		treePaneSelector = new TreePaneSelector(treePane);
131 	}
132 
getCurrentTree()133 	public Tree getCurrentTree() {
134 		return trees.get(currentTreeIndex);
135 	}
136 
137 
getCurrentTreeIndex()138 	public int getCurrentTreeIndex() {
139 		return currentTreeIndex;
140 	}
141 
getTreeCount()142 	public int getTreeCount() {
143 		if (trees == null) return 0;
144 		return trees.size();
145 	}
146 
getStatusProvider()147     public StatusProvider getStatusProvider() {
148         return null;
149     }
150 
showTree(int index)151     public void showTree(int index) {
152 		int i = index;
153 		for (TreePane treePane : treePanes) {
154 			if (i < trees.size()) {
155 				Tree tree = trees.get(i);
156 
157 				if (tree instanceof RootedTree) {
158 					treePane.setTree((RootedTree)tree);
159 				} else {
160 					treePane.setTree(Utils.rootTheTree(tree));
161 				}
162 			} else {
163 				treePane.setTree(null);
164 			}
165 			i++;
166 		}
167 		currentTreeIndex = index;
168 
169 		treePanePanel.removeAll();
170 		setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
171 		for (TreePane treePane : treePanes) {
172 			treePanePanel.add(treePane);
173 			setupTreePane(treePane);
174 		}
175 
176 		fireTreeChanged();
177 	}
178 
showNextTree()179 	public void showNextTree() {
180 		if (currentTreeIndex < trees.size() - 1) {
181 			showTree(currentTreeIndex + 1);
182 		}
183 	}
184 
showPreviousTree()185 	public void showPreviousTree() {
186 		if (currentTreeIndex > 0) {
187 			showTree(currentTreeIndex - 1);
188 		}
189 	}
190 
setTreeLayout(TreeLayout treeLayout)191 	public void setTreeLayout(TreeLayout treeLayout) {
192 		for (TreePane treePane : treePanes) {
193 			treePane.setTreeLayout(treeLayout);
194 		}
195 	}
196 
197 	private boolean zoomPending = false;
198 	private double zoom = 0.0, verticalExpansion = 0.0;
199 
setZoom(double zoom)200 	public void setZoom(double zoom) {
201 		this.zoom = zoom * MAX_ZOOM;
202 		refreshZoom();
203 	}
204 
setVerticalExpansion(double verticalExpansion)205 	public void setVerticalExpansion(double verticalExpansion) {
206 		this.verticalExpansion = verticalExpansion * MAX_VERTICAL_EXPANSION;
207 		refreshZoom();
208 	}
209 
verticalExpansionAllowed()210 	public boolean verticalExpansionAllowed() {
211 		return !treePanes.get(0).maintainAspectRatio();
212 	}
213 
setTimeScale(TimeScale timeScale)214 	public void setTimeScale(TimeScale timeScale) {
215 		for (TreePane treePane : treePanes) {
216 			treePane.setTimeScale(timeScale);
217 		}
218 	}
219 
refreshZoom()220 	private void refreshZoom() {
221 		setZoom(zoom, zoom + verticalExpansion);
222 	}
223 
setZoom(double xZoom, double yZoom)224 	private void setZoom(double xZoom, double yZoom) {
225 
226 		Dimension viewportSize = viewport.getViewSize();
227 		Point position = viewport.getViewPosition();
228 
229 		Dimension extentSize = viewport.getExtentSize();
230 		double w = extentSize.getWidth() * (1.0 + xZoom);
231 		double h = extentSize.getHeight() * (1.0 + yZoom);
232 
233 		Dimension newSize = new Dimension((int) w, (int) h / treesPerPage);
234 		for (TreePane treePane : treePanes) {
235 			treePane.setPreferredSize(newSize);
236 			treePane.revalidate();
237 		}
238 
239 		double cx = position.getX() + (0.5 * extentSize.getWidth());
240 		double cy = position.getY() + (0.5 * extentSize.getHeight());
241 
242 		double rx = ((double) newSize.getWidth()) / viewportSize.getWidth();
243 		double ry = ((double) newSize.getHeight()) / viewportSize.getHeight();
244 
245 		double px = (cx * rx) - (extentSize.getWidth() / 2.0);
246 		double py = (cy * ry) - (extentSize.getHeight() / 2.0);
247 
248 		Point newPosition = new Point((int) px, (int) py);
249 		viewport.setViewPosition(newPosition);
250 	}
251 
hasSelection()252 	public boolean hasSelection() {
253 		for (TreePane treePane : treePanes) {
254 			if (treePane.hasSelection()) return true;
255 		}
256 		return false;
257 	}
258 
getSelectedNodes()259 	public Set<Node> getSelectedNodes() {
260 		for (TreePane treePane : treePanes) {
261 			if (treePane.hasSelection()) return treePane.getSelectedNodes();
262 		}
263 		return Collections.emptySet();
264 	}
265 
getSelectedTips()266 	public Set<Node> getSelectedTips() {
267 		for (TreePane treePane : treePanes) {
268 			if (treePane.hasSelection()) return treePane.getSelectedTips();
269 		}
270 		return Collections.emptySet();
271 	}
272 
selectTaxa(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive)273 	public void selectTaxa(String attributeName, TextSearchType searchType, String searchString, boolean caseSensitive) {
274 	}
275 
selectNodes(String attribute, TextSearchType searchType, String searchString, boolean caseSensitive)276 	public void selectNodes(String attribute, TextSearchType searchType, String searchString, boolean caseSensitive) {
277 	}
278 
selectTaxa(String attributeName, NumberSearchType searchType, Number searchValue)279 	public void selectTaxa(String attributeName, NumberSearchType searchType, Number searchValue) {
280 	}
281 
selectNodes(String attributeName, NumberSearchType searchType, Number searchValue)282 	public void selectNodes(String attributeName, NumberSearchType searchType, Number searchValue) {
283 	}
284 
selectTaxa(final Collection<String> taxonNames)285     public void selectTaxa(final Collection<String> taxonNames) {
286     }
287 
collapseSelectedNodes()288     public void collapseSelectedNodes() {
289 //         treePane.collapseSelectedNodes();
290 	}
291 
annotateSelectedNodes(String name, Object value)292 	public void annotateSelectedNodes(String name, Object value) {
293 //        treePane.annotateSelectedNodes(name, value);
294 		fireTreeSettingsChanged();
295 	}
296 
annotateSelectedTips(String name, Object value)297 	public void annotateSelectedTips(String name, Object value) {
298 		//       treePane.annotateSelectedTips(name, value);
299 		fireTreeSettingsChanged();
300 	}
301 
selectAll()302 	public void selectAll() {
303 //        if (treePaneSelector.getSelectionMode() == TreePaneSelector.SelectionMode.TAXA) {
304 //            treePane.selectAllTaxa();
305 //        } else {
306 //            treePane.selectAllNodes();
307 //        }
308 	}
309 
clearSelectedTaxa()310 	public void clearSelectedTaxa() {
311 //        treePane.clearSelection();
312 	}
313 
addTreeSelectionListener(TreeSelectionListener treeSelectionListener)314 	public void addTreeSelectionListener(TreeSelectionListener treeSelectionListener) {
315 		for (TreePane treePane : treePanes) {
316 			treePane.addTreeSelectionListener(treeSelectionListener);
317 		}
318 	}
319 
removeTreeSelectionListener(TreeSelectionListener treeSelectionListener)320 	public void removeTreeSelectionListener(TreeSelectionListener treeSelectionListener) {
321 		for (TreePane treePane : treePanes) {
322 			treePane.removeTreeSelectionListener(treeSelectionListener);
323 		}
324 	}
325 
setSelectionMode(TreePaneSelector.SelectionMode selectionMode)326 	public void setSelectionMode(TreePaneSelector.SelectionMode selectionMode) {
327 //        TreePaneSelector.SelectionMode oldSelectionMode = treePaneSelector.getSelectionMode();
328 //
329 //        if (selectionMode == oldSelectionMode) {
330 //            return;
331 //        }
332 //
333 //        if (oldSelectionMode == TreePaneSelector.SelectionMode.TAXA) {
334 //            treePane.selectNodesFromSelectedTips();
335 //        } else if (selectionMode == TreePaneSelector.SelectionMode.TAXA) {
336 //            treePane.selectTipsFromSelectedNodes();
337 //        } else if (selectionMode == TreePaneSelector.SelectionMode.CLADE) {
338 //            treePane.selectCladesFromSelectedNodes();
339 //        }
340 //        treePaneSelector.setSelectionMode(selectionMode);
341 	}
342 
setDragMode(TreePaneSelector.DragMode dragMode)343 	public void setDragMode(TreePaneSelector.DragMode dragMode) {
344 		treePaneSelector.setDragMode(dragMode);
345 	}
346 
347 	// A load of deligated method calls through to treePane (which is now hidden outside the package).
setTipLabelPainter(LabelPainter<Node> tipLabelPainter)348 	public void setTipLabelPainter(LabelPainter<Node> tipLabelPainter) {
349 		for (TreePane treePane : treePanes) {
350 			treePane.setTipLabelPainter(tipLabelPainter);
351 		}
352 //		tipLabelPainter.setupAttributes(trees);
353 		fireTreeSettingsChanged();
354 	}
355 
setNodeLabelPainter(LabelPainter<Node> nodeLabelPainter)356 	public void setNodeLabelPainter(LabelPainter<Node> nodeLabelPainter) {
357 		for (TreePane treePane : treePanes) {
358 			treePane.setNodeLabelPainter(nodeLabelPainter);
359 		}
360 //		nodeLabelPainter.setupAttributes(trees);
361 		fireTreeSettingsChanged();
362 	}
363 
setNodeBarPainter(NodeBarPainter nodeBarPainter)364 	public void setNodeBarPainter(NodeBarPainter nodeBarPainter) {
365 		for (TreePane treePane : treePanes) {
366 			treePane.setNodeBarPainter(nodeBarPainter);
367 		}
368 //		nodeBarPainter.setupAttributes(trees);
369 		fireTreeSettingsChanged();
370 	}
371 
setNodeShapePainter(NodeShapePainter nodeShapePainter)372     public void setNodeShapePainter(NodeShapePainter nodeShapePainter) {
373         for (TreePane treePane : treePanes) {
374             treePane.setNodeShapePainter(nodeShapePainter);
375         }
376         fireTreeSettingsChanged();
377     }
378 
setBranchLabelPainter(LabelPainter<Node> branchLabelPainter)379     public void setBranchLabelPainter(LabelPainter<Node> branchLabelPainter) {
380 		for (TreePane treePane : treePanes) {
381 			treePane.setBranchLabelPainter(branchLabelPainter);
382 		}
383 		fireTreeSettingsChanged();
384 	}
385 
addScalePainter(ScalePainter scalePainter)386 	public void addScalePainter(ScalePainter scalePainter) {
387 		for (TreePane treePane : treePanes) {
388 			treePane.addScalePainter(scalePainter);
389 		}
390 		fireTreeSettingsChanged();
391 	}
392 
removeScalePainter(ScalePainter scalePainter)393 	public void removeScalePainter(ScalePainter scalePainter) {
394 		for (TreePane treePane : treePanes) {
395 			treePane.removeScalePainter(scalePainter);
396 		}
397 		fireTreeSettingsChanged();
398 	}
399 
setScaleGridPainter(ScaleGridPainter scaleGridPainter)400     public void setScaleGridPainter(ScaleGridPainter scaleGridPainter) {
401         for (TreePane treePane : treePanes) {
402             treePane.setScaleGridPainter(scaleGridPainter);
403         }
404         fireTreeSettingsChanged();
405     }
406 
setLegendPainter(LegendPainter legendPainter)407     public void setLegendPainter(LegendPainter legendPainter) {
408         for (TreePane treePane : treePanes) {
409             treePane.setLegendPainter(legendPainter);
410         }
411 //        legendPainter.setupAttributes(trees);
412         fireTreeSettingsChanged();
413     }
414 
setBranchDecorator(Decorator branchDecorator, boolean isGradient)415     public void setBranchDecorator(Decorator branchDecorator, boolean isGradient) {
416 		for (TreePane treePane : treePanes) {
417 			treePane.setBranchDecorator(branchDecorator, isGradient);
418 		}
419 		fireTreeSettingsChanged();
420 	}
421 
setBranchColouringDecorator(String branchColouringAttribute, Decorator branchColouringDecorator)422 	public void setBranchColouringDecorator(String branchColouringAttribute, Decorator branchColouringDecorator) {
423 		for (TreePane treePane : treePanes) {
424 			treePane.setBranchColouringDecorator(branchColouringAttribute, branchColouringDecorator);
425 		}
426 		fireTreeSettingsChanged();
427 	}
428 
setNodeBackgroundDecorator(Decorator nodeBackgroundDecorator)429     public void setNodeBackgroundDecorator(Decorator nodeBackgroundDecorator) {
430         for (TreePane treePane : treePanes) {
431             treePane.setNodeBackgroundDecorator(nodeBackgroundDecorator);
432         }
433         fireTreeSettingsChanged();
434     }
435 
setHilightingGradient(boolean hilightingGradient)436     public void setHilightingGradient(boolean hilightingGradient) {
437         for (TreePane treePane : treePanes) {
438             treePane.setHilightingGradient(hilightingGradient);
439         }
440     }
441 
setSelectionColor(Color selectionColor)442     public void setSelectionColor(Color selectionColor) {
443 		for (TreePane treePane : treePanes) {
444 			treePane.setSelectionColor(selectionColor);
445 		}
446 		fireTreeSettingsChanged();
447 	}
448 
getSelectionPaint()449 	public Paint getSelectionPaint() {
450 		return treePanes.get(0).getSelectionPaint();
451 	}
452 
setBranchStroke(BasicStroke branchStroke)453 	public void setBranchStroke(BasicStroke branchStroke) {
454 		for (TreePane treePane : treePanes) {
455 			treePane.setBranchStroke(branchStroke);
456 		}
457 		fireTreeSettingsChanged();
458 	}
459 
isTransformBranchesOn()460 	public boolean isTransformBranchesOn() {
461 		return treePanes.get(0).isTransformBranchesOn();
462 	}
463 
getBranchTransform()464 	public TransformedRootedTree.Transform getBranchTransform() {
465 		return treePanes.get(0).getBranchTransform();
466 	}
467 
setTransformBranchesOn(boolean transformBranchesOn)468 	public void setTransformBranchesOn(boolean transformBranchesOn) {
469 		for (TreePane treePane : treePanes) {
470 			treePane.setTransformBranchesOn(transformBranchesOn);
471 		}
472 		fireTreeSettingsChanged();
473 	}
474 
setBranchTransform(TransformedRootedTree.Transform transform)475 	public void setBranchTransform(TransformedRootedTree.Transform transform) {
476 		for (TreePane treePane : treePanes) {
477 			treePane.setBranchTransform(transform);
478 		}
479 		fireTreeSettingsChanged();
480 	}
481 
isOrderBranchesOn()482 	public boolean isOrderBranchesOn() {
483 		return treePanes.get(0).isOrderBranchesOn();
484 	}
485 
getBranchOrdering()486 	public SortedRootedTree.BranchOrdering getBranchOrdering() {
487 		return treePanes.get(0).getBranchOrdering();
488 	}
489 
setOrderBranchesOn(boolean orderBranchesOn)490 	public void setOrderBranchesOn(boolean orderBranchesOn) {
491 		for (TreePane treePane : treePanes) {
492 			treePane.setOrderBranchesOn(orderBranchesOn);
493 		}
494 		fireTreeSettingsChanged();
495 	}
496 
setBranchOrdering(SortedRootedTree.BranchOrdering branchOrdering)497 	public void setBranchOrdering(SortedRootedTree.BranchOrdering branchOrdering) {
498 		for (TreePane treePane : treePanes) {
499 			treePane.setBranchOrdering(branchOrdering);
500 		}
501 		fireTreeSettingsChanged();
502 	}
503 
isRootingOn()504     public boolean isRootingOn() {
505         return treePanes.get(0).isOrderBranchesOn();
506     }
507 
getRootingType()508     public TreePane.RootingType getRootingType() {
509         return treePanes.get(0).getRootingType();
510     }
511 
setRootingOn(boolean rootingOn)512     public void setRootingOn(boolean rootingOn) {
513         for (TreePane treePane : treePanes) {
514             treePane.setRootingOn(rootingOn);
515         }
516         fireTreeSettingsChanged();
517     }
518 
setRootingType(TreePane.RootingType rootingType)519     public void setRootingType(TreePane.RootingType rootingType) {
520         for (TreePane treePane : treePanes) {
521             treePane.setRootingType(rootingType);
522         }
523         fireTreeSettingsChanged();
524     }
525 
getContentPane()526 	public JComponent getContentPane() {
527 		return treePanePanel;
528 	}
529 
paint(Graphics g)530 	public void paint(Graphics g) {
531 		if( zoomPending  ) {
532 			refreshZoom();
533 			zoomPending = false;
534 		}
535 		super.paint(g);
536 	}
537 
print(Graphics g, PageFormat pageFormat, int pageIndex)538 	public int print(Graphics g, PageFormat pageFormat, int pageIndex) throws PrinterException {
539 		return treePanePanel.print(g, pageFormat, pageIndex);
540 	}
541 
addTreeViewerListener(TreeViewerListener listener)542 	public void addTreeViewerListener(TreeViewerListener listener) {
543 		listeners.add(listener);
544 	}
545 
removeTreeViewerListener(TreeViewerListener listener)546 	public void removeTreeViewerListener(TreeViewerListener listener) {
547 		listeners.remove(listener);
548 	}
549 
fireTreeChanged()550 	public void fireTreeChanged() {
551 		for (TreeViewerListener listener : listeners) {
552 			listener.treeChanged();
553 		}
554 	}
555 
fireTreeSettingsChanged()556 	public void fireTreeSettingsChanged() {
557 		for (TreeViewerListener listener : listeners) {
558 			listener.treeSettingsChanged();
559 		}
560 	}
561 
562 	private java.util.List<TreeViewerListener> listeners = new ArrayList<TreeViewerListener>();
563 
564 	private java.util.List<Tree> trees = new ArrayList<Tree>();
565 	private java.util.List<TreePane> treePanes = new ArrayList<TreePane>();
566 	private int currentTreeIndex = 0;
567 	private int treesPerPage = 1;
568 
569 	private MultiPaneTreePanel treePanePanel;
570 	protected TreePaneSelector treePaneSelector;
571 	protected JViewport viewport;
572 
573 	class MultiPaneTreePanel extends JPanel implements Printable {
574 
print(Graphics graphics, PageFormat pageFormat, int i)575 		public int print(Graphics graphics, PageFormat pageFormat, int i) throws PrinterException {
576 			return 0;
577 		}
578 	}
579 }
580