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