JAL-1632 refactored NJTree constructor to include SimilarityParams
[jalview.git] / src / jalview / gui / TreePanel.java
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 jalview.analysis.AlignmentSorter;
24 import jalview.analysis.NJTree;
25 import jalview.analysis.scoremodels.ScoreModels;
26 import jalview.analysis.scoremodels.SimilarityParams;
27 import jalview.api.analysis.ScoreModelI;
28 import jalview.api.analysis.ViewBasedAnalysisI;
29 import jalview.bin.Cache;
30 import jalview.commands.CommandI;
31 import jalview.commands.OrderCommand;
32 import jalview.datamodel.Alignment;
33 import jalview.datamodel.AlignmentI;
34 import jalview.datamodel.AlignmentView;
35 import jalview.datamodel.BinaryNode;
36 import jalview.datamodel.ColumnSelection;
37 import jalview.datamodel.DBRefEntry;
38 import jalview.datamodel.NodeTransformI;
39 import jalview.datamodel.SequenceFeature;
40 import jalview.datamodel.SequenceI;
41 import jalview.datamodel.SequenceNode;
42 import jalview.io.JalviewFileChooser;
43 import jalview.io.JalviewFileView;
44 import jalview.io.NewickFile;
45 import jalview.jbgui.GTreePanel;
46 import jalview.util.ImageMaker;
47 import jalview.util.MessageManager;
48 import jalview.viewmodel.AlignmentViewport;
49
50 import java.awt.Font;
51 import java.awt.Graphics;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.ActionListener;
54 import java.awt.image.BufferedImage;
55 import java.beans.PropertyChangeEvent;
56 import java.io.FileOutputStream;
57 import java.util.ArrayList;
58 import java.util.List;
59
60 import javax.imageio.ImageIO;
61 import javax.swing.ButtonGroup;
62 import javax.swing.JMenuItem;
63 import javax.swing.JRadioButtonMenuItem;
64
65 import org.jibble.epsgraphics.EpsGraphics2D;
66
67 /**
68  * DOCUMENT ME!
69  * 
70  * @author $author$
71  * @version $Revision$
72  */
73 public class TreePanel extends GTreePanel
74 {
75   String type;
76
77   String pwtype;
78
79   TreeCanvas treeCanvas;
80
81   NJTree tree;
82
83   AlignViewport av;
84
85   /**
86    * Creates a new TreePanel object.
87    * 
88    * @param av
89    *          DOCUMENT ME!
90    * @param seqVector
91    *          DOCUMENT ME!
92    * @param type
93    *          DOCUMENT ME!
94    * @param pwtype
95    *          DOCUMENT ME!
96    * @param s
97    *          DOCUMENT ME!
98    * @param e
99    *          DOCUMENT ME!
100    */
101   public TreePanel(AlignmentPanel ap, String type, String pwtype)
102   {
103     super();
104     initTreePanel(ap, type, pwtype, null, null);
105
106     // We know this tree has distances. JBPNote TODO: prolly should add this as
107     // a userdefined default
108     // showDistances(true);
109   }
110
111   public TreePanel(AlignmentPanel av, String type, String pwtype,
112           NewickFile newtree, AlignmentView inputData)
113   {
114     super();
115     initTreePanel(av, type, pwtype, newtree, inputData);
116   }
117
118   public AlignmentI getAlignment()
119   {
120     return treeCanvas.av.getAlignment();
121   }
122
123   public AlignmentViewport getViewPort()
124   {
125     return treeCanvas.av;
126   }
127
128   void initTreePanel(AlignmentPanel ap, String type, String pwtype,
129           NewickFile newTree, AlignmentView inputData)
130   {
131
132     av = ap.av;
133     this.type = type;
134     this.pwtype = pwtype;
135
136     treeCanvas = new TreeCanvas(this, ap, scrollPane);
137     scrollPane.setViewportView(treeCanvas);
138
139     PaintRefresher.Register(this, ap.av.getSequenceSetId());
140
141     buildAssociatedViewMenu();
142
143     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()
144     {
145       @Override
146       public void propertyChange(PropertyChangeEvent evt)
147       {
148         if (evt.getPropertyName().equals("alignment"))
149         {
150           if (tree == null)
151           {
152             System.out.println("tree is null");
153             // TODO: deal with case when a change event is received whilst a
154             // tree is still being calculated - should save reference for
155             // processing message later.
156             return;
157           }
158           if (evt.getNewValue() == null)
159           {
160             System.out
161                     .println("new alignment sequences vector value is null");
162           }
163
164           tree.updatePlaceHolders((List<SequenceI>) evt.getNewValue());
165           treeCanvas.nameHash.clear(); // reset the mapping between canvas
166           // rectangles and leafnodes
167           repaint();
168         }
169       }
170     });
171
172     TreeLoader tl = new TreeLoader(newTree);
173     if (inputData != null)
174     {
175       tl.odata = inputData;
176     }
177     tl.start();
178
179   }
180
181   @Override
182   public void viewMenu_menuSelected()
183   {
184     buildAssociatedViewMenu();
185   }
186
187   void buildAssociatedViewMenu()
188   {
189     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(av
190             .getSequenceSetId());
191     if (aps.length == 1 && treeCanvas.ap == aps[0])
192     {
193       associateLeavesMenu.setVisible(false);
194       return;
195     }
196
197     associateLeavesMenu.setVisible(true);
198
199     if ((viewMenu.getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
200     {
201       viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
202     }
203
204     associateLeavesMenu.removeAll();
205
206     JRadioButtonMenuItem item;
207     ButtonGroup buttonGroup = new ButtonGroup();
208     int i, iSize = aps.length;
209     final TreePanel thisTreePanel = this;
210     for (i = 0; i < iSize; i++)
211     {
212       final AlignmentPanel ap = aps[i];
213       item = new JRadioButtonMenuItem(ap.av.viewName, ap == treeCanvas.ap);
214       buttonGroup.add(item);
215       item.addActionListener(new ActionListener()
216       {
217         @Override
218         public void actionPerformed(ActionEvent evt)
219         {
220           treeCanvas.applyToAllViews = false;
221           treeCanvas.ap = ap;
222           treeCanvas.av = ap.av;
223           PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId());
224         }
225       });
226
227       associateLeavesMenu.add(item);
228     }
229
230     final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
231             MessageManager.getString("label.all_views"));
232     buttonGroup.add(itemf);
233     itemf.setSelected(treeCanvas.applyToAllViews);
234     itemf.addActionListener(new ActionListener()
235     {
236       @Override
237       public void actionPerformed(ActionEvent evt)
238       {
239         treeCanvas.applyToAllViews = itemf.isSelected();
240       }
241     });
242     associateLeavesMenu.add(itemf);
243
244   }
245
246   class TreeLoader extends Thread
247   {
248     NewickFile newtree;
249
250     jalview.datamodel.AlignmentView odata = null;
251
252     public TreeLoader(NewickFile newtree)
253     {
254       this.newtree = newtree;
255       if (newtree != null)
256       {
257         // Must be outside run(), as Jalview2XML tries to
258         // update distance/bootstrap visibility at the same time
259         showBootstrap(newtree.HasBootstrap());
260         showDistances(newtree.HasDistances());
261       }
262     }
263
264     @Override
265     public void run()
266     {
267
268       if (newtree != null)
269       {
270         if (odata == null)
271         {
272           tree = new NJTree(av.getAlignment().getSequencesArray(), newtree);
273         }
274         else
275         {
276           tree = new NJTree(av.getAlignment().getSequencesArray(), odata,
277                   newtree);
278         }
279         if (!tree.hasOriginalSequenceData())
280         {
281           allowOriginalSeqData(false);
282         }
283       }
284       else
285       {
286         int start, end;
287         SequenceI[] seqs;
288         boolean selview = av.getSelectionGroup() != null
289                 && av.getSelectionGroup().getSize() > 1;
290         AlignmentView seqStrings = av.getAlignmentView(selview);
291         if (!selview)
292         {
293           start = 0;
294           end = av.getAlignment().getWidth();
295           seqs = av.getAlignment().getSequencesArray();
296         }
297         else
298         {
299           start = av.getSelectionGroup().getStartRes();
300           end = av.getSelectionGroup().getEndRes() + 1;
301           seqs = av.getSelectionGroup().getSequencesInOrder(
302                   av.getAlignment());
303         }
304         ScoreModelI sm = configureScoreModel(pwtype);
305         tree = new NJTree(av, type, sm, SimilarityParams.Jalview);
306         showDistances(true);
307       }
308
309       tree.reCount(tree.getTopNode());
310       tree.findHeight(tree.getTopNode());
311       treeCanvas.setTree(tree);
312       treeCanvas.repaint();
313       av.setCurrentTree(tree);
314       if (av.getSortByTree())
315       {
316         sortByTree_actionPerformed();
317       }
318     }
319   }
320
321   public void showDistances(boolean b)
322   {
323     treeCanvas.setShowDistances(b);
324     distanceMenu.setSelected(b);
325   }
326
327   public void showBootstrap(boolean b)
328   {
329     treeCanvas.setShowBootstrap(b);
330     bootstrapMenu.setSelected(b);
331   }
332
333   public void showPlaceholders(boolean b)
334   {
335     placeholdersMenu.setState(b);
336     treeCanvas.setMarkPlaceholders(b);
337   }
338
339   private void allowOriginalSeqData(boolean b)
340   {
341     originalSeqData.setVisible(b);
342   }
343
344   /**
345    * DOCUMENT ME!
346    * 
347    * @return DOCUMENT ME!
348    */
349   public NJTree getTree()
350   {
351     return tree;
352   }
353
354   /**
355    * DOCUMENT ME!
356    * 
357    * @param e
358    *          DOCUMENT ME!
359    */
360   @Override
361   public void textbox_actionPerformed(ActionEvent e)
362   {
363     CutAndPasteTransfer cap = new CutAndPasteTransfer();
364
365     String newTitle = getPanelTitle(type, pwtype);
366
367     NewickFile fout = new NewickFile(tree.getTopNode());
368     try
369     {
370       cap.setText(fout.print(tree.hasBootstrap(), tree.hasDistances(),
371               tree.hasRootDistance()));
372       Desktop.addInternalFrame(cap, newTitle, 500, 100);
373     } catch (OutOfMemoryError oom)
374     {
375       new OOMWarning("generating newick tree file", oom);
376       cap.dispose();
377     }
378
379   }
380
381   /**
382    * DOCUMENT ME!
383    * 
384    * @param e
385    *          DOCUMENT ME!
386    */
387   @Override
388   public void saveAsNewick_actionPerformed(ActionEvent e)
389   {
390     JalviewFileChooser chooser = new JalviewFileChooser(
391             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
392     chooser.setFileView(new JalviewFileView());
393     chooser.setDialogTitle(MessageManager
394             .getString("label.save_tree_as_newick"));
395     chooser.setToolTipText(MessageManager.getString("action.save"));
396
397     int value = chooser.showSaveDialog(null);
398
399     if (value == JalviewFileChooser.APPROVE_OPTION)
400     {
401       String choice = chooser.getSelectedFile().getPath();
402       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
403               .getSelectedFile().getParent());
404
405       try
406       {
407         jalview.io.NewickFile fout = new jalview.io.NewickFile(
408                 tree.getTopNode());
409         String output = fout.print(tree.hasBootstrap(),
410                 tree.hasDistances(), tree.hasRootDistance());
411         java.io.PrintWriter out = new java.io.PrintWriter(
412                 new java.io.FileWriter(choice));
413         out.println(output);
414         out.close();
415       } catch (Exception ex)
416       {
417         ex.printStackTrace();
418       }
419     }
420   }
421
422   /**
423    * DOCUMENT ME!
424    * 
425    * @param e
426    *          DOCUMENT ME!
427    */
428   @Override
429   public void printMenu_actionPerformed(ActionEvent e)
430   {
431     // Putting in a thread avoids Swing painting problems
432     treeCanvas.startPrinting();
433   }
434
435   @Override
436   public void originalSeqData_actionPerformed(ActionEvent e)
437   {
438     if (!tree.hasOriginalSequenceData())
439     {
440       jalview.bin.Cache.log
441               .info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
442       return;
443     }
444     // decide if av alignment is sufficiently different to original data to
445     // warrant a new window to be created
446     // create new alignmnt window with hidden regions (unhiding hidden regions
447     // yields unaligned seqs)
448     // or create a selection box around columns in alignment view
449     // test Alignment(SeqCigar[])
450     char gc = '-';
451     try
452     {
453       // we try to get the associated view's gap character
454       // but this may fail if the view was closed...
455       gc = av.getGapCharacter();
456
457     } catch (Exception ex)
458     {
459     }
460     ;
461     Object[] alAndColsel = tree.seqData.getAlignmentAndColumnSelection(gc);
462
463     if (alAndColsel != null && alAndColsel[0] != null)
464     {
465       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
466
467       AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
468       AlignmentI dataset = (av != null && av.getAlignment() != null) ? av
469               .getAlignment().getDataset() : null;
470       if (dataset != null)
471       {
472         al.setDataset(dataset);
473       }
474
475       if (true)
476       {
477         // make a new frame!
478         AlignFrame af = new AlignFrame(al,
479                 (ColumnSelection) alAndColsel[1], AlignFrame.DEFAULT_WIDTH,
480                 AlignFrame.DEFAULT_HEIGHT);
481
482         // >>>This is a fix for the moment, until a better solution is
483         // found!!<<<
484         // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
485
486         // af.addSortByOrderMenuItem(ServiceName + " Ordering",
487         // msaorder);
488
489         Desktop.addInternalFrame(af, MessageManager.formatMessage(
490                 "label.original_data_for_params",
491                 new Object[] { this.title }), AlignFrame.DEFAULT_WIDTH,
492                 AlignFrame.DEFAULT_HEIGHT);
493       }
494     }
495   }
496
497   /**
498    * DOCUMENT ME!
499    * 
500    * @param e
501    *          DOCUMENT ME!
502    */
503   @Override
504   public void fitToWindow_actionPerformed(ActionEvent e)
505   {
506     treeCanvas.fitToWindow = fitToWindow.isSelected();
507     repaint();
508   }
509
510   /**
511    * sort the associated alignment view by the current tree.
512    * 
513    * @param e
514    */
515   @Override
516   public void sortByTree_actionPerformed()
517   {
518
519     if (treeCanvas.applyToAllViews)
520     {
521       final ArrayList<CommandI> commands = new ArrayList<CommandI>();
522       for (AlignmentPanel ap : PaintRefresher.getAssociatedPanels(av
523               .getSequenceSetId()))
524       {
525         commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
526       }
527       av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
528       {
529
530         @Override
531         public void undoCommand(AlignmentI[] views)
532         {
533           for (CommandI tsort : commands)
534           {
535             tsort.undoCommand(views);
536           }
537         }
538
539         @Override
540         public int getSize()
541         {
542           return commands.size();
543         }
544
545         @Override
546         public String getDescription()
547         {
548           return "Tree Sort (many views)";
549         }
550
551         @Override
552         public void doCommand(AlignmentI[] views)
553         {
554
555           for (CommandI tsort : commands)
556           {
557             tsort.doCommand(views);
558           }
559         }
560       });
561       for (AlignmentPanel ap : PaintRefresher.getAssociatedPanels(av
562               .getSequenceSetId()))
563       {
564         // ensure all the alignFrames refresh their GI after adding an undo item
565         ap.alignFrame.updateEditMenuBar();
566       }
567     }
568     else
569     {
570       treeCanvas.ap.alignFrame
571               .addHistoryItem(sortAlignmentIn(treeCanvas.ap));
572     }
573
574   }
575
576   public CommandI sortAlignmentIn(AlignmentPanel ap)
577   {
578     AlignmentViewport av = ap.av;
579     SequenceI[] oldOrder = av.getAlignment().getSequencesArray();
580     AlignmentSorter.sortByTree(av.getAlignment(), tree);
581     CommandI undo;
582     undo = new OrderCommand("Tree Sort", oldOrder, av.getAlignment());
583
584     ap.paintAlignment(true);
585     return undo;
586   }
587
588   /**
589    * DOCUMENT ME!
590    * 
591    * @param e
592    *          DOCUMENT ME!
593    */
594   @Override
595   public void font_actionPerformed(ActionEvent e)
596   {
597     if (treeCanvas == null)
598     {
599       return;
600     }
601
602     new FontChooser(this);
603   }
604
605   public Font getTreeFont()
606   {
607     return treeCanvas.font;
608   }
609
610   public void setTreeFont(Font font)
611   {
612     if (treeCanvas != null)
613     {
614       treeCanvas.setFont(font);
615     }
616   }
617
618   /**
619    * DOCUMENT ME!
620    * 
621    * @param e
622    *          DOCUMENT ME!
623    */
624   @Override
625   public void distanceMenu_actionPerformed(ActionEvent e)
626   {
627     treeCanvas.setShowDistances(distanceMenu.isSelected());
628   }
629
630   /**
631    * DOCUMENT ME!
632    * 
633    * @param e
634    *          DOCUMENT ME!
635    */
636   @Override
637   public void bootstrapMenu_actionPerformed(ActionEvent e)
638   {
639     treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
640   }
641
642   /**
643    * DOCUMENT ME!
644    * 
645    * @param e
646    *          DOCUMENT ME!
647    */
648   @Override
649   public void placeholdersMenu_actionPerformed(ActionEvent e)
650   {
651     treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
652   }
653
654   /**
655    * DOCUMENT ME!
656    * 
657    * @param e
658    *          DOCUMENT ME!
659    */
660   @Override
661   public void epsTree_actionPerformed(ActionEvent e)
662   {
663     boolean accurateText = true;
664
665     String renderStyle = jalview.bin.Cache.getDefault("EPS_RENDERING",
666             "Prompt each time");
667
668     // If we need to prompt, and if the GUI is visible then
669     // Prompt for EPS rendering style
670     if (renderStyle.equalsIgnoreCase("Prompt each time")
671             && !(System.getProperty("java.awt.headless") != null && System
672                     .getProperty("java.awt.headless").equals("true")))
673     {
674       EPSOptions eps = new EPSOptions();
675       renderStyle = eps.getValue();
676
677       if (renderStyle == null || eps.cancelled)
678       {
679         return;
680       }
681
682     }
683
684     if (renderStyle.equalsIgnoreCase("text"))
685     {
686       accurateText = false;
687     }
688
689     int width = treeCanvas.getWidth();
690     int height = treeCanvas.getHeight();
691
692     try
693     {
694       JalviewFileChooser chooser = new JalviewFileChooser(
695               ImageMaker.EPS_EXTENSION, ImageMaker.EPS_EXTENSION);
696       chooser.setFileView(new JalviewFileView());
697       chooser.setDialogTitle(MessageManager
698               .getString("label.create_eps_from_tree"));
699       chooser.setToolTipText(MessageManager.getString("action.save"));
700
701       int value = chooser.showSaveDialog(this);
702
703       if (value != JalviewFileChooser.APPROVE_OPTION)
704       {
705         return;
706       }
707
708       Cache.setProperty("LAST_DIRECTORY", chooser.getSelectedFile()
709               .getParent());
710
711       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
712       EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width, height);
713
714       pg.setAccurateTextMode(accurateText);
715
716       treeCanvas.draw(pg, width, height);
717
718       pg.flush();
719       pg.close();
720     } catch (Exception ex)
721     {
722       ex.printStackTrace();
723     }
724   }
725
726   /**
727    * DOCUMENT ME!
728    * 
729    * @param e
730    *          DOCUMENT ME!
731    */
732   @Override
733   public void pngTree_actionPerformed(ActionEvent e)
734   {
735     int width = treeCanvas.getWidth();
736     int height = treeCanvas.getHeight();
737
738     try
739     {
740       JalviewFileChooser chooser = new JalviewFileChooser(
741               ImageMaker.PNG_EXTENSION, ImageMaker.PNG_DESCRIPTION);
742
743       chooser.setFileView(new jalview.io.JalviewFileView());
744       chooser.setDialogTitle(MessageManager
745               .getString("label.create_png_from_tree"));
746       chooser.setToolTipText(MessageManager.getString("action.save"));
747
748       int value = chooser.showSaveDialog(this);
749
750       if (value != jalview.io.JalviewFileChooser.APPROVE_OPTION)
751       {
752         return;
753       }
754
755       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
756               .getSelectedFile().getParent());
757
758       FileOutputStream out = new FileOutputStream(chooser.getSelectedFile());
759
760       BufferedImage bi = new BufferedImage(width, height,
761               BufferedImage.TYPE_INT_RGB);
762       Graphics png = bi.getGraphics();
763
764       treeCanvas.draw(png, width, height);
765
766       ImageIO.write(bi, "png", out);
767       out.close();
768     } catch (Exception ex)
769     {
770       ex.printStackTrace();
771     }
772   }
773
774   /**
775    * change node labels to the annotation referred to by labelClass TODO:
776    * promote to a datamodel modification that can be undone TODO: make argument
777    * one case of a generic transformation function ie { undoStep = apply(Tree,
778    * TransformFunction)};
779    * 
780    * @param labelClass
781    */
782   public void changeNames(final String labelClass)
783   {
784     tree.applyToNodes(new NodeTransformI()
785     {
786
787       @Override
788       public void transform(BinaryNode node)
789       {
790         if (node instanceof SequenceNode
791                 && !((SequenceNode) node).isPlaceholder()
792                 && !((SequenceNode) node).isDummy())
793         {
794           String newname = null;
795           SequenceI sq = (SequenceI) ((SequenceNode) node).element();
796           if (sq != null)
797           {
798             // search dbrefs, features and annotation
799             DBRefEntry[] refs = jalview.util.DBRefUtils.selectRefs(
800                     sq.getDBRefs(),
801                     new String[] { labelClass.toUpperCase() });
802             if (refs != null)
803             {
804               for (int i = 0; i < refs.length; i++)
805               {
806                 if (newname == null)
807                 {
808                   newname = new String(refs[i].getAccessionId());
809                 }
810                 else
811                 {
812                   newname = newname + "; " + refs[i].getAccessionId();
813                 }
814               }
815             }
816             if (newname == null)
817             {
818               SequenceFeature sf[] = sq.getSequenceFeatures();
819               for (int i = 0; sf != null && i < sf.length; i++)
820               {
821                 if (sf[i].getType().equals(labelClass))
822                 {
823                   if (newname == null)
824                   {
825                     newname = new String(sf[i].getDescription());
826                   }
827                   else
828                   {
829                     newname = newname + "; " + sf[i].getDescription();
830                   }
831                 }
832               }
833             }
834           }
835           if (newname != null)
836           {
837             String oldname = ((SequenceNode) node).getName();
838             // TODO : save in the undo object for this modification.
839             ((SequenceNode) node).setName(newname);
840           }
841         }
842       }
843     });
844   }
845
846   /**
847    * Formats a localised title for the tree panel, like
848    * <p>
849    * Neighbour Joining Using BLOSUM62
850    * <p>
851    * For a tree loaded from file, just uses the file name
852    * 
853    * @param treeType
854    *          NJ or AV or FromFile
855    * @param modelOrFileName
856    * @return
857    */
858   public static String getPanelTitle(String treeType, String modelOrFileName)
859   {
860     if (NJTree.FROM_FILE.equals(treeType))
861     {
862       return modelOrFileName;
863     }
864
865     /*
866      * i18n description of Neighbour Joining or Average Distance method
867      */
868     String treecalcnm = MessageManager.getString("label.tree_calc_"
869             + treeType.toLowerCase());
870
871     /*
872      * i18n description (if any) of score model used
873      */
874     String smn = MessageManager.getStringOrReturn("label.score_model_",
875             modelOrFileName);
876
877     /*
878      * put them together as <method> Using <model>
879      */
880     final String ttl = MessageManager.formatMessage("label.treecalc_title",
881             treecalcnm, smn);
882     return ttl;
883   }
884
885   /**
886    * Gets the score model for the given name. If the score model is one that
887    * requires to get state data from the current view, allow it to do so
888    * 
889    * @param sm
890    * @return
891    */
892   protected ScoreModelI configureScoreModel(String modelName)
893   {
894     ScoreModelI sm = ScoreModels.getInstance().forName(modelName);
895     if (sm instanceof ViewBasedAnalysisI)
896     {
897       try
898       {
899         sm = sm.getClass().newInstance();
900         ((ViewBasedAnalysisI) sm).configureFromAlignmentView(treeCanvas.ap);
901       } catch (Exception q)
902       {
903         Cache.log.error("Couldn't create a scoremodel instance for "
904                 + sm.getName());
905       }
906     }
907     return sm;
908   }
909 }