Clover icon

Coverage Report

  1. Project Clover database Thu Nov 7 2024 10:11:34 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,025
716
102
0.39
4.64
28
1.82

Classes

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