1. Project Clover database Fri Dec 6 2024 13:47:14 GMT
  2. Package jalview.gui

File TreePanel.java

 

Coverage histogram

../../img/srcFileCovDistChart4.png
49% of files have more coverage

Code metrics

66
260
56
2
1,026
717
102
0.39
4.64
28
1.82

Classes

Class
Line #
Actions
TreePanel 97 235 93
0.3177842531.8%
TreePanel.TreeLoader 422 25 9
0.6410256664.1%
 

Contributing tests

This file is covered by 10 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.gui;
22   
23    import java.awt.BorderLayout;
24    import java.awt.Color;
25    import java.awt.Dimension;
26    import java.awt.FlowLayout;
27    import java.awt.Font;
28    import java.awt.FontMetrics;
29    import java.awt.Graphics;
30    import java.awt.GridLayout;
31    import java.awt.event.ActionEvent;
32    import java.awt.event.ActionListener;
33    import java.awt.event.KeyAdapter;
34    import java.awt.event.KeyEvent;
35    import java.beans.PropertyChangeEvent;
36    import java.beans.PropertyChangeListener;
37    import java.io.File;
38    import java.io.FileOutputStream;
39    import java.util.ArrayList;
40    import java.util.HashMap;
41    import java.util.List;
42    import java.util.Locale;
43    import java.util.Map;
44   
45    import javax.swing.ButtonGroup;
46    import javax.swing.JLabel;
47    import javax.swing.JMenuItem;
48    import javax.swing.JPanel;
49    import javax.swing.JRadioButtonMenuItem;
50    import javax.swing.JScrollPane;
51    import javax.swing.SwingConstants;
52    import javax.swing.event.InternalFrameAdapter;
53    import javax.swing.event.InternalFrameEvent;
54   
55    import org.jibble.epsgraphics.EpsGraphics2D;
56   
57    import jalview.analysis.AlignmentSorter;
58    import jalview.analysis.AlignmentUtils;
59    import jalview.analysis.AverageDistanceTree;
60    import jalview.analysis.NJTree;
61    import jalview.analysis.TreeBuilder;
62    import jalview.analysis.TreeModel;
63    import jalview.analysis.scoremodels.ScoreModels;
64    import jalview.api.analysis.ScoreModelI;
65    import jalview.api.analysis.SimilarityParamsI;
66    import jalview.bin.Cache;
67    import jalview.bin.Console;
68    import jalview.commands.CommandI;
69    import jalview.commands.OrderCommand;
70    import jalview.datamodel.Alignment;
71    import jalview.datamodel.AlignmentAnnotation;
72    import jalview.datamodel.AlignmentI;
73    import jalview.datamodel.AlignmentView;
74    import jalview.datamodel.BinaryNode;
75    import jalview.datamodel.DBRefEntry;
76    import jalview.datamodel.HiddenColumns;
77    import jalview.datamodel.NodeTransformI;
78    import jalview.datamodel.SequenceFeature;
79    import jalview.datamodel.SequenceI;
80    import jalview.datamodel.SequenceNode;
81    import jalview.gui.ImageExporter.ImageWriterI;
82    import jalview.io.JalviewFileChooser;
83    import jalview.io.JalviewFileView;
84    import jalview.io.NewickFile;
85    import jalview.io.exceptions.ImageOutputException;
86    import jalview.jbgui.GTreePanel;
87    import jalview.util.ImageMaker.TYPE;
88    import jalview.util.MessageManager;
89    import jalview.viewmodel.AlignmentViewport;
90   
91    /**
92    * DOCUMENT ME!
93    *
94    * @author $author$
95    * @version $Revision$
96    */
 
97    public class TreePanel extends GTreePanel
98    {
99    String treeType;
100   
101    String scoreModelName; // if tree computed
102   
103    String treeTitle; // if tree loaded
104   
105    SimilarityParamsI similarityParams;
106   
107    private TreeCanvas treeCanvas;
108   
109    TreeModel tree;
110   
111    private AlignViewport av;
112   
113    // New JLabel for subtitle
114    private JLabel subtitleLabel;
115   
116    private Map<String, Color> secondaryStructureProviderColorMap;
117   
118    /**
119    * Creates a new TreePanel object.
120    *
121    * @param ap
122    * @param type
123    * @param modelName
124    * @param options
125    */
 
126  0 toggle public TreePanel(AlignmentPanel ap, String type, String modelName,
127    SimilarityParamsI options)
128    {
129  0 super();
130  0 this.setFrameIcon(null);
131  0 this.similarityParams = options;
132  0 initTreePanel(ap, type, modelName, null, null, null);
133   
134    // We know this tree has distances. JBPNote TODO: prolly should add this as
135    // a userdefined default
136    // showDistances(true);
137    }
138   
 
139  14 toggle public TreePanel(AlignmentPanel alignPanel, NewickFile newtree,
140    String theTitle, AlignmentView inputData)
141    {
142  14 this(alignPanel, newtree, theTitle, inputData, null, null);
143    }
144   
145    /**
146    * when true, leaf labels are annotations
147    */
148    boolean forAnnotation = false;
149   
 
150  14 toggle public TreePanel(AlignmentPanel alignPanel, NewickFile newtree,
151    String theTitle, AlignmentView inputData,
152    AlignmentAnnotation[] leafAnnotations, String subTitle)
153    {
154  14 super();
155  14 this.forAnnotation = leafAnnotations != null
156    && leafAnnotations.length > 0;
157  14 this.setFrameIcon(null);
158  14 this.treeTitle = theTitle;
159  14 initTreePanel(alignPanel, null, null, newtree, inputData, null);
160    }
161   
162    /**
163    * columnwise tree associated with positions in aa
164    *
165    * @param alignPanel
166    * @param fin
167    * @param title
168    * @param aa
169    */
 
170  0 toggle public TreePanel(AlignmentPanel alignPanel, NewickFile fin,
171    AlignmentAnnotation aa, String title)
172    {
173  0 super();
174  0 columnWise = true;
175  0 assocAnnotation = aa;
176  0 this.setFrameIcon(null);
177  0 this.treeTitle = title;
178  0 initTreePanel(alignPanel, null, null, fin, null, null);
179    }
180   
181    boolean columnWise = false;
182   
183    AlignmentAnnotation assocAnnotation = null;
184   
 
185  174 toggle public boolean isColumnWise()
186    {
187  174 return columnWise;
188    }
189   
 
190  14 toggle public AlignmentAnnotation getAssocAnnotation()
191    {
192  14 return assocAnnotation;
193    }
194   
 
195  0 toggle public AlignmentI getAlignment()
196    {
197  0 return getTreeCanvas().getViewport().getAlignment();
198    }
199   
 
200  0 toggle public AlignmentViewport getViewPort()
201    {
202    // @Mungo - Why don't we return our own viewport ???
203  0 return getTreeCanvas().getViewport();
204    }
205   
206   
 
207  0 toggle public Map<String, Color> getSecondaryStructureProviderColorMap()
208    {
209  0 return secondaryStructureProviderColorMap;
210    }
211   
 
212  0 toggle private void addSubtitlePanel(String subTitle)
213    {
214  0 subtitleLabel = new JLabel(subTitle, SwingConstants.LEFT);
215   
216  0 JPanel panel = new JPanel(new BorderLayout());
217  0 panel.add(subtitleLabel, BorderLayout.NORTH);
218  0 panel.add(scrollPane, BorderLayout.CENTER);
219   
220  0 this.add(panel);
221    }
222   
 
223  14 toggle void initTreePanel(AlignmentPanel ap, String type, String modelName,
224    NewickFile newTree, AlignmentView inputData,
225    AlignmentAnnotation[] leafAnnotations)
226    {
227   
228  14 av = ap.av;
229  14 this.treeType = type;
230  14 this.scoreModelName = modelName;
231   
232  14 treeCanvas = new TreeCanvas(this, ap, scrollPane);
233  14 scrollPane.setViewportView(treeCanvas);
234   
235  14 if (similarityParams != null
236    && similarityParams.getSecondaryStructureSource() != null)
237    {
238    //setting showSecondaryStructureProviderMenu to true if the
239    //similarity is based on secondary structure
240  0 showSecondaryStructureProviderMenu.setVisible(true);
241  0 addSubtitlePanel(" Secondary Structure Provider : "
242    + similarityParams.getSecondaryStructureSource());
243   
244    }
245    else {
246    //setting showSecondaryStructureProviderMenu to false if the
247    //similarity is not based on secondary structure
248  14 showSecondaryStructureProviderMenu.setVisible(false);
249    }
250  14 if (leafAnnotations != null)
251    {
252  0 forAnnotation = true;
253    }
254  14 if (columnWise)
255    {
256  0 bootstrapMenu.setVisible(false);
257  0 placeholdersMenu.setState(false);
258  0 placeholdersMenu.setVisible(false);
259  0 fitToWindow.setState(false);
260  0 sortAssocViews.setVisible(false);
261    }
262   
263  14 addKeyListener(new KeyAdapter()
264    {
 
265  0 toggle @Override
266    public void keyPressed(KeyEvent e)
267    {
268  0 switch (e.getKeyCode())
269    {
270  0 case 27: // escape
271  0 treeCanvas.clearSelectedLeaves();
272  0 e.consume();
273  0 break;
274   
275    }
276   
277    }
278    });
279  14 PaintRefresher.Register(this, ap.av.getSequenceSetId());
280   
281  14 buildAssociatedViewMenu();
282   
283  14 final PropertyChangeListener listener = addAlignmentListener();
284   
285    /*
286    * remove listener when window is closed, so that this
287    * panel can be garbage collected
288    */
289  14 addInternalFrameListener(new InternalFrameAdapter()
290    {
 
291  12 toggle @Override
292    public void internalFrameClosed(InternalFrameEvent evt)
293    {
294  12 if (av != null)
295    {
296  12 av.removePropertyChangeListener(listener);
297    }
298  12 releaseReferences();
299    }
300    });
301   
302  14 TreeLoader tl = new TreeLoader(newTree, inputData, leafAnnotations);
303  14 tl.start();
304   
305    }
306   
307    /**
308    * Ensure any potentially large object references are nulled
309    */
 
310  12 toggle public void releaseReferences()
311    {
312  12 this.tree = null;
313  12 this.treeCanvas.tree = null;
314  12 this.treeCanvas.nodeHash = null;
315  12 this.treeCanvas.nameHash = null;
316    }
317   
318    /**
319    * @return
320    */
 
321  14 toggle protected PropertyChangeListener addAlignmentListener()
322    {
323  14 final PropertyChangeListener listener = new PropertyChangeListener()
324    {
 
325  0 toggle @Override
326    public void propertyChange(PropertyChangeEvent evt)
327    {
328  0 if (evt.getPropertyName().equals("alignment"))
329    {
330  0 if (tree == null)
331    {
332  0 jalview.bin.Console.outPrintln("tree is null");
333    // TODO: deal with case when a change event is received whilst a
334    // tree is still being calculated - should save reference for
335    // processing message later.
336  0 return;
337    }
338  0 if (evt.getNewValue() == null)
339    {
340  0 jalview.bin.Console.outPrintln(
341    "new alignment sequences vector value is null");
342    }
343   
344  0 tree.updatePlaceHolders((List<SequenceI>) evt.getNewValue());
345  0 treeCanvas.nameHash.clear(); // reset the mapping between canvas
346    // rectangles and leafnodes
347  0 repaint();
348    }
349    }
350    };
351  14 av.addPropertyChangeListener(listener);
352  14 return listener;
353    }
354   
 
355  0 toggle @Override
356    public void viewMenu_menuSelected()
357    {
358  0 buildAssociatedViewMenu();
359    }
360   
 
361  14 toggle void buildAssociatedViewMenu()
362    {
363  14 AlignmentPanel[] aps = PaintRefresher
364    .getAssociatedPanels(av.getSequenceSetId());
365  14 if (aps.length == 1 && getTreeCanvas().getAssociatedPanel() == aps[0])
366    {
367  6 associateLeavesMenu.setVisible(false);
368  6 return;
369    }
370   
371  8 associateLeavesMenu.setVisible(true);
372   
373  8 if ((viewMenu
374    .getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
375    {
376  8 viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
377    }
378   
379  8 associateLeavesMenu.removeAll();
380   
381  8 JRadioButtonMenuItem item;
382  8 ButtonGroup buttonGroup = new ButtonGroup();
383  8 int i, iSize = aps.length;
384  8 final TreePanel thisTreePanel = this;
385  32 for (i = 0; i < iSize; i++)
386    {
387  24 final AlignmentPanel ap = aps[i];
388  24 item = new JRadioButtonMenuItem(ap.av.getViewName(),
389    ap == treeCanvas.getAssociatedPanel());
390  24 buttonGroup.add(item);
391  24 item.addActionListener(new ActionListener()
392    {
 
393  0 toggle @Override
394    public void actionPerformed(ActionEvent evt)
395    {
396  0 treeCanvas.applyToAllViews = false;
397  0 treeCanvas.setAssociatedPanel(ap);
398  0 treeCanvas.setViewport(ap.av);
399  0 PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId());
400    }
401    });
402   
403  24 associateLeavesMenu.add(item);
404    }
405   
406  8 final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
407    MessageManager.getString("label.all_views"));
408  8 buttonGroup.add(itemf);
409  8 itemf.setSelected(treeCanvas.applyToAllViews);
410  8 itemf.addActionListener(new ActionListener()
411    {
 
412  0 toggle @Override
413    public void actionPerformed(ActionEvent evt)
414    {
415  0 treeCanvas.applyToAllViews = itemf.isSelected();
416    }
417    });
418  8 associateLeavesMenu.add(itemf);
419   
420    }
421   
 
422    class TreeLoader extends Thread
423    {
424    private NewickFile newtree;
425   
426    private AlignmentView odata = null;
427   
428    private AlignmentAnnotation[] leafAnnotations;
429   
 
430  14 toggle public TreeLoader(NewickFile newickFile, AlignmentView inputData,
431    AlignmentAnnotation[] leafAnnotations)
432    {
433  14 this.newtree = newickFile;
434  14 this.odata = inputData;
435  14 this.leafAnnotations = leafAnnotations;
436   
437  14 if (newickFile != null)
438    {
439    // Must be outside run(), as Jalview2XML tries to
440    // update distance/bootstrap visibility at the same time
441  14 showBootstrap(newickFile.HasBootstrap());
442  14 showDistances(newickFile.HasDistances());
443    }
444    }
445   
 
446  14 toggle @Override
447    public void run()
448    {
449   
450  14 if (newtree != null)
451    {
452  14 tree = new TreeModel(av.getAlignment().getSequencesArray(), odata,
453    newtree, leafAnnotations);
454  14 if (tree.getOriginalData() == null)
455    {
456  14 originalSeqData.setVisible(false);
457    }
458    }
459    else
460    {
461  0 ScoreModelI sm = ScoreModels.getInstance().getScoreModel(
462    scoreModelName, treeCanvas.getAssociatedPanel());
463  0 TreeBuilder njtree = treeType.equals(TreeBuilder.NEIGHBOUR_JOINING)
464    ? new NJTree(av, sm, similarityParams)
465    : new AverageDistanceTree(av, sm, similarityParams);
466  0 List<String> labels = njtree.getLabels();
467  0 if(labels != null && labels.size()>0) {
468  0 secondaryStructureProviderColorMap = new HashMap<String, Color>();
469  0 AlignmentUtils.assignSecondaryStructureProviderColor(secondaryStructureProviderColorMap, labels);
470    }
471  0 tree = new TreeModel(njtree);
472    // don't display distances for columnwise trees
473    }
474  14 showDistances(!columnWise);
475  14 tree.reCount(tree.getTopNode());
476  14 tree.findHeight(tree.getTopNode());
477  14 treeCanvas.setTree(tree);
478  14 treeCanvas.repaint();
479  14 av.setCurrentTree(tree);
480  14 if (av.getSortByTree())
481    {
482  1 sortByTree_actionPerformed();
483    }
484    }
485    }
486   
 
487  40 toggle public void showDistances(boolean b)
488    {
489  40 treeCanvas.setShowDistances(b);
490  40 distanceMenu.setSelected(b);
491    }
492   
 
493  26 toggle public void showBootstrap(boolean b)
494    {
495  26 treeCanvas.setShowBootstrap(b);
496  26 bootstrapMenu.setSelected(b);
497    }
498   
 
499  12 toggle public void showPlaceholders(boolean b)
500    {
501  12 placeholdersMenu.setState(b);
502  12 treeCanvas.setMarkPlaceholders(b);
503    }
504   
505    /**
506    * DOCUMENT ME!
507    *
508    * @return DOCUMENT ME!
509    */
 
510  22 toggle public TreeModel getTree()
511    {
512  22 return tree;
513    }
514   
515    /**
516    * DOCUMENT ME!
517    *
518    * @param e
519    * DOCUMENT ME!
520    */
 
521  0 toggle @Override
522    public void textbox_actionPerformed(ActionEvent e)
523    {
524  0 CutAndPasteTransfer cap = new CutAndPasteTransfer();
525   
526  0 String newTitle = getPanelTitle();
527   
528  0 NewickFile fout = new NewickFile(tree.getTopNode());
529  0 try
530    {
531  0 cap.setText(fout.print(tree.hasBootstrap(), tree.hasDistances(),
532    tree.hasRootDistance()));
533  0 Desktop.addInternalFrame(cap, newTitle, 500, 100);
534    } catch (OutOfMemoryError oom)
535    {
536  0 new OOMWarning("generating newick tree file", oom);
537  0 cap.dispose();
538    }
539   
540    }
541   
542    /**
543    * DOCUMENT ME!
544    *
545    * @param e
546    * DOCUMENT ME!
547    */
 
548  0 toggle @Override
549    public void saveAsNewick_actionPerformed(ActionEvent e)
550    {
551    // TODO: JAL-3048 save newick file for Jalview-JS
552  0 JalviewFileChooser chooser = new JalviewFileChooser(
553    Cache.getProperty("LAST_DIRECTORY"));
554  0 chooser.setFileView(new JalviewFileView());
555  0 chooser.setDialogTitle(
556    MessageManager.getString("label.save_tree_as_newick"));
557  0 chooser.setToolTipText(MessageManager.getString("action.save"));
558   
559  0 int value = chooser.showSaveDialog(null);
560   
561  0 if (value == JalviewFileChooser.APPROVE_OPTION)
562    {
563  0 String choice = chooser.getSelectedFile().getPath();
564  0 Cache.setProperty("LAST_DIRECTORY",
565    chooser.getSelectedFile().getParent());
566   
567  0 try
568    {
569  0 jalview.io.NewickFile fout = new jalview.io.NewickFile(
570    tree.getTopNode());
571  0 String output = fout.print(tree.hasBootstrap(), tree.hasDistances(),
572    tree.hasRootDistance());
573  0 java.io.PrintWriter out = new java.io.PrintWriter(
574    new java.io.FileWriter(choice));
575  0 out.println(output);
576  0 out.close();
577    } catch (Exception ex)
578    {
579  0 ex.printStackTrace();
580    }
581    }
582    }
583   
584    /**
585    * DOCUMENT ME!
586    *
587    * @param e
588    * DOCUMENT ME!
589    */
 
590  0 toggle @Override
591    public void printMenu_actionPerformed(ActionEvent e)
592    {
593    // Putting in a thread avoids Swing painting problems
594  0 treeCanvas.startPrinting();
595    }
596   
 
597  0 toggle @Override
598    public void originalSeqData_actionPerformed(ActionEvent e)
599    {
600  0 AlignmentView originalData = tree.getOriginalData();
601  0 if (originalData == null)
602    {
603  0 Console.info(
604    "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
605  0 return;
606    }
607    // decide if av alignment is sufficiently different to original data to
608    // warrant a new window to be created
609    // create new alignmnt window with hidden regions (unhiding hidden regions
610    // yields unaligned seqs)
611    // or create a selection box around columns in alignment view
612    // test Alignment(SeqCigar[])
613  0 char gc = '-';
614  0 try
615    {
616    // we try to get the associated view's gap character
617    // but this may fail if the view was closed...
618  0 gc = av.getGapCharacter();
619   
620    } catch (Exception ex)
621    {
622    }
623   
624  0 Object[] alAndColsel = originalData.getAlignmentAndHiddenColumns(gc);
625   
626  0 if (alAndColsel != null && alAndColsel[0] != null)
627    {
628    // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
629   
630  0 AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
631  0 AlignmentI dataset = (av != null && av.getAlignment() != null)
632    ? av.getAlignment().getDataset()
633    : null;
634  0 if (dataset != null)
635    {
636  0 al.setDataset(dataset);
637    }
638   
639  0 if (true)
640    {
641    // make a new frame!
642  0 AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1],
643    AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
644   
645    // >>>This is a fix for the moment, until a better solution is
646    // found!!<<<
647    // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
648   
649    // af.addSortByOrderMenuItem(ServiceName + " Ordering",
650    // msaorder);
651   
652  0 Desktop.addInternalFrame(af, MessageManager.formatMessage(
653    "label.original_data_for_params", new Object[]
654    { this.title }), AlignFrame.DEFAULT_WIDTH,
655    AlignFrame.DEFAULT_HEIGHT);
656    }
657    }
658    }
659   
660    /**
661    * DOCUMENT ME!
662    *
663    * @param e
664    * DOCUMENT ME!
665    */
 
666  12 toggle @Override
667    public void fitToWindow_actionPerformed(ActionEvent e)
668    {
669  12 treeCanvas.fitToWindow = fitToWindow.isSelected();
670  12 repaint();
671    }
672   
673    /**
674    * sort the associated alignment view by the current tree.
675    *
676    * @param e
677    */
 
678  1 toggle @Override
679    public void sortByTree_actionPerformed()
680    {
681   
682  1 if (treeCanvas.applyToAllViews)
683    {
684  0 final ArrayList<CommandI> commands = new ArrayList<>();
685  0 for (AlignmentPanel ap : PaintRefresher
686    .getAssociatedPanels(av.getSequenceSetId()))
687    {
688  0 commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
689    }
690  0 av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
691    {
692   
 
693  0 toggle @Override
694    public void undoCommand(AlignmentI[] views)
695    {
696  0 for (CommandI tsort : commands)
697    {
698  0 tsort.undoCommand(views);
699    }
700    }
701   
 
702  0 toggle @Override
703    public int getSize()
704    {
705  0 return commands.size();
706    }
707   
 
708  0 toggle @Override
709    public String getDescription()
710    {
711  0 return "Tree Sort (many views)";
712    }
713   
 
714  0 toggle @Override
715    public void doCommand(AlignmentI[] views)
716    {
717   
718  0 for (CommandI tsort : commands)
719    {
720  0 tsort.doCommand(views);
721    }
722    }
723    });
724  0 for (AlignmentPanel ap : PaintRefresher
725    .getAssociatedPanels(av.getSequenceSetId()))
726    {
727    // ensure all the alignFrames refresh their GI after adding an undo item
728  0 ap.alignFrame.updateEditMenuBar();
729    }
730    }
731    else
732    {
733  1 treeCanvas.getAssociatedPanel().alignFrame.addHistoryItem(
734    sortAlignmentIn(treeCanvas.getAssociatedPanel()));
735    }
736   
737    }
738   
 
739  1 toggle public CommandI sortAlignmentIn(AlignmentPanel ap)
740    {
741    // TODO: move to alignment view controller
742  1 AlignmentViewport viewport = ap.av;
743  1 SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
744  1 AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
745  1 CommandI undo;
746  1 undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment());
747   
748  1 ap.paintAlignment(true, false);
749  1 return undo;
750    }
751   
752    /**
753    * DOCUMENT ME!
754    *
755    * @param e
756    * DOCUMENT ME!
757    */
 
758  0 toggle @Override
759    public void font_actionPerformed(ActionEvent e)
760    {
761  0 if (treeCanvas == null)
762    {
763  0 return;
764    }
765   
766  0 new FontChooser(this);
767    }
768   
 
769  12 toggle public Font getTreeFont()
770    {
771  12 return treeCanvas.font;
772    }
773   
 
774  12 toggle public void setTreeFont(Font f)
775    {
776  12 if (treeCanvas != null)
777    {
778  12 treeCanvas.setFont(f);
779    }
780    }
781   
782    /**
783    * DOCUMENT ME!
784    *
785    * @param e
786    * DOCUMENT ME!
787    */
 
788  0 toggle @Override
789    public void distanceMenu_actionPerformed(ActionEvent e)
790    {
791  0 treeCanvas.setShowDistances(distanceMenu.isSelected());
792    }
793   
 
794  0 toggle @Override
795    public void hideStructureProviders_actionPerformed(ActionEvent e)
796    {
797  0 treeCanvas.hideStructureProviders(hideStructureProviders.isSelected());
798    }
799   
 
800  0 toggle @Override
801    public void showStructureProviderColouredLines_actionPerformed(ActionEvent e)
802    {
803  0 treeCanvas.setShowStructureProviderColouredLines(showStructureProviderColouredLines.isSelected());
804    }
805   
 
806  0 toggle @Override
807    public void showStructureProviderLabels_actionPerformed(ActionEvent e)
808    {
809  0 treeCanvas.setShowStructureProviderLabels(showStructureProviderLabels.isSelected());
810    }
811   
812    /**
813    * DOCUMENT ME!
814    *
815    * @param e
816    * DOCUMENT ME!
817    */
 
818  0 toggle @Override
819    public void bootstrapMenu_actionPerformed(ActionEvent e)
820    {
821  0 treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
822    }
823   
824    /**
825    * DOCUMENT ME!
826    *
827    * @param e
828    * DOCUMENT ME!
829    */
 
830  0 toggle @Override
831    public void placeholdersMenu_actionPerformed(ActionEvent e)
832    {
833  0 treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
834    }
835   
836    /**
837    * Outputs the Tree in image format (currently EPS or PNG). The user is
838    * prompted for the file to save to, and for EPS (unless a preference is
839    * already set) for the choice of Text or Lineart for character rendering.
840    */
 
841  0 toggle @Override
842    public void writeTreeImage(TYPE imageFormat)
843    {
844  0 int width = treeCanvas.getWidth();
845  0 int height = treeCanvas.getHeight();
846  0 ImageWriterI writer = new ImageWriterI()
847    {
 
848  0 toggle @Override
849    public void exportImage(Graphics g) throws Exception
850    {
851  0 treeCanvas.draw(g, width, height);
852    }
853    };
854  0 String tree = MessageManager.getString("label.tree");
855  0 ImageExporter exporter = new ImageExporter(writer, null, imageFormat,
856    tree);
857  0 try
858    {
859  0 exporter.doExport(null, this, width, height,
860    tree.toLowerCase(Locale.ROOT));
861    } catch (ImageOutputException ioex)
862    {
863  0 Console.error(
864    "Unexpected error whilst writing " + imageFormat.toString(),
865    ioex);
866    }
867    }
868   
869    /**
870    * change node labels to the annotation referred to by labelClass TODO:
871    * promote to a datamodel modification that can be undone TODO: make argument
872    * one case of a generic transformation function ie { undoStep = apply(Tree,
873    * TransformFunction)};
874    *
875    * @param labelClass
876    */
 
877  0 toggle public void changeNames(final String labelClass)
878    {
879  0 tree.applyToNodes(new NodeTransformI()
880    {
881   
 
882  0 toggle @Override
883    public void transform(BinaryNode node)
884    {
885  0 if (node instanceof SequenceNode
886    && !((SequenceNode) node).isPlaceholder()
887    && !((SequenceNode) node).isDummy())
888    {
889  0 String newname = null;
890  0 SequenceI sq = (SequenceI) ((BinaryNode) node).element();
891  0 if (sq != null)
892    {
893    // search dbrefs, features and annotation
894  0 List<DBRefEntry> refs = jalview.util.DBRefUtils
895    .selectRefs(sq.getDBRefs(), new String[]
896    { labelClass.toUpperCase(Locale.ROOT) });
897  0 if (refs != null)
898    {
899  0 for (int i = 0, ni = refs.size(); i < ni; i++)
900    {
901  0 if (newname == null)
902    {
903  0 newname = new String(refs.get(i).getAccessionId());
904    }
905    else
906    {
907  0 newname += "; " + refs.get(i).getAccessionId();
908    }
909    }
910    }
911  0 if (newname == null)
912    {
913  0 List<SequenceFeature> features = sq.getFeatures()
914    .getPositionalFeatures(labelClass);
915  0 for (SequenceFeature feature : features)
916    {
917  0 if (newname == null)
918    {
919  0 newname = feature.getDescription();
920    }
921    else
922    {
923  0 newname = newname + "; " + feature.getDescription();
924    }
925    }
926    }
927    }
928  0 if (newname != null)
929    {
930    // String oldname = ((SequenceNode) node).getName();
931    // TODO : save oldname in the undo object for this modification.
932  0 ((BinaryNode) node).setName(newname);
933    }
934    }
935    }
936    });
937    }
938   
939    /**
940    * Formats a localised title for the tree panel, like
941    * <p>
942    * Neighbour Joining Using BLOSUM62
943    * <p>
944    * For a tree loaded from file, just uses the file name
945    *
946    * @return
947    */
 
948  0 toggle public String getPanelTitle()
949    {
950  0 if (treeTitle != null)
951    {
952  0 return treeTitle;
953    }
954   
955    /*
956    * i18n description of Neighbour Joining or Average Distance method
957    */
958  0 String treecalcnm = MessageManager.getString(
959    "label.tree_calc_" + treeType.toLowerCase(Locale.ROOT));
960   
961    /*
962    * short score model name (long description can be too long)
963    */
964  0 String smn = scoreModelName;
965   
966    /*
967    * put them together as <method> Using <model>
968    */
969  0 final String ttl = MessageManager.formatMessage("label.calc_title",
970    treecalcnm, smn);
971  0 return ttl;
972    }
973   
974    /**
975    * Builds an EPS image and writes it to the specified file.
976    *
977    * @param outFile
978    * @param textOption
979    * true for Text character rendering, false for Lineart
980    */
 
981  0 toggle protected void writeEpsFile(File outFile, boolean textOption)
982    {
983  0 try
984    {
985  0 int width = treeCanvas.getWidth();
986  0 int height = treeCanvas.getHeight();
987   
988  0 FileOutputStream out = new FileOutputStream(outFile);
989  0 EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width,
990    height);
991  0 pg.setAccurateTextMode(!textOption);
992  0 treeCanvas.draw(pg, width, height);
993   
994  0 pg.flush();
995  0 pg.close();
996    } catch (Exception ex)
997    {
998  0 jalview.bin.Console.errPrintln("Error writing tree as EPS");
999  0 ex.printStackTrace();
1000    }
1001    }
1002   
 
1003  0 toggle public AlignViewport getViewport()
1004    {
1005  0 return av;
1006    }
1007   
 
1008  0 toggle public void setViewport(AlignViewport av)
1009    {
1010  0 this.av = av;
1011    }
1012   
 
1013  42 toggle public TreeCanvas getTreeCanvas()
1014    {
1015  42 return treeCanvas;
1016    }
1017   
 
1018  0 toggle public static TreePanel newTreeForAnnotations(AlignmentPanel alignPanel,
1019    NewickFile nf, String treeTitle2, AlignmentView input)
1020    {
1021    // TODO - recover subpanel name
1022  0 TreePanel tp = new TreePanel(alignPanel, nf, treeTitle2, input, null,
1023    "Annotation Tree");
1024  0 return tp;
1025    }
1026    }