Clover icon

Coverage Report

  1. Project Clover database Thu May 28 2026 15:40:39 BST
  2. Package jalview.gui

File TreePanel.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

94
397
72
2
1,468
988
138
0.35
5.51
36
1.92

Classes

Class Line # Actions
TreePanel 110 366 126
0.5058365550.6%
TreePanel.TreeLoader 649 31 12
0.918367391.8%
 

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.CardLayout;
25    import java.awt.Color;
26    import java.awt.Component;
27    import java.awt.Dimension;
28    import java.awt.Font;
29    import java.awt.Graphics;
30    import java.awt.Insets;
31    import java.awt.event.ActionEvent;
32    import java.awt.event.ActionListener;
33    import java.awt.event.ItemEvent;
34    import java.awt.event.KeyAdapter;
35    import java.awt.event.KeyEvent;
36    import java.beans.PropertyChangeEvent;
37    import java.beans.PropertyChangeListener;
38    import java.io.File;
39    import java.io.FileOutputStream;
40    import java.util.ArrayList;
41    import java.util.Arrays;
42    import java.util.List;
43    import java.util.Locale;
44    import java.util.Map;
45    import java.util.Vector;
46   
47    import javax.swing.AbstractButton;
48    import javax.swing.BorderFactory;
49    import javax.swing.Box;
50    import javax.swing.BoxLayout;
51    import javax.swing.ButtonGroup;
52    import javax.swing.Icon;
53    import javax.swing.JMenuItem;
54    import javax.swing.JPanel;
55    import javax.swing.JRadioButtonMenuItem;
56    import javax.swing.JScrollBar;
57    import javax.swing.JScrollPane;
58    import javax.swing.JToggleButton;
59    import javax.swing.border.BevelBorder;
60    import javax.swing.event.InternalFrameAdapter;
61    import javax.swing.event.InternalFrameEvent;
62   
63    import org.jibble.epsgraphics.EpsGraphics2D;
64   
65    import jalview.analysis.AlignmentAnnotationUtils;
66    import jalview.analysis.AlignmentSorter;
67    import jalview.analysis.AlignmentUtils;
68    import jalview.analysis.AnnotationSorter;
69    import jalview.analysis.AverageDistanceTree;
70    import jalview.analysis.NJTree;
71    import jalview.analysis.TreeBuilder;
72    import jalview.analysis.TreeModel;
73    import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
74    import jalview.analysis.scoremodels.ScoreModels;
75    import jalview.api.analysis.ScoreModelI;
76    import jalview.api.analysis.SimilarityParamsI;
77    import jalview.bin.Cache;
78    import jalview.bin.Console;
79    import jalview.commands.CommandI;
80    import jalview.commands.OrderCommand;
81    import jalview.datamodel.Alignment;
82    import jalview.datamodel.AlignmentAnnotation;
83    import jalview.datamodel.AlignmentI;
84    import jalview.datamodel.AlignmentView;
85    import jalview.datamodel.BinaryNode;
86    import jalview.datamodel.DBRefEntry;
87    import jalview.datamodel.HiddenColumns;
88    import jalview.datamodel.NodeTransformI;
89    import jalview.datamodel.SequenceFeature;
90    import jalview.datamodel.SequenceI;
91    import jalview.datamodel.SequenceNode;
92    import jalview.gui.ImageExporter.ImageWriterI;
93    import jalview.io.JalviewFileChooser;
94    import jalview.io.JalviewFileView;
95    import jalview.io.NewickFile;
96    import jalview.io.exceptions.ImageOutputException;
97    import jalview.jbgui.GTreePanel;
98    import jalview.util.ImageMaker.TYPE;
99    import jalview.util.ColorUtils;
100    import jalview.util.Constants;
101    import jalview.util.MessageManager;
102    import jalview.viewmodel.AlignmentViewport;
103   
104    /**
105    * DOCUMENT ME!
106    *
107    * @author $author$
108    * @version $Revision$
109    */
 
110    public class TreePanel extends GTreePanel
111    {
112    String treeType;
113   
114    String scoreModelName; // if tree computed
115   
116    String treeTitle; // if tree loaded
117   
118    SimilarityParamsI similarityParams;
119   
120    private TreeCanvas treeCanvas;
121   
122    TreeModel tree;
123   
124    private AlignViewport av;
125   
126    //if tree is based on annotations like secondary structure
127    boolean annotationBased = false;
128   
129    JPanel ssProviderPanel;
130   
131    private Map<String, Color> secondaryStructureProviderColorMap;
132   
133    private static final int GAP_WIDTH = 2; //gap between components
134    private static final int SS_PROVIDER_PANEL_FONT_SIZE = 10;
135    private static final int TOGGLE_BUTTON_ICON_WIDTH = 10;
136    private static final int TOGGLE_BUTTON_ICON_HEIGHT = 10;
137    private static final int SCROLLBAR_HEIGHT = 8;
138    private static final int SCROLL_PANEL_HEIGHT = 30;
139    private static final int TOGGLE_BUTTON_MARGIN = 1;
140    private static final int NO_PADDING = 0;
141    private static final int NO_WIDTH_PREFERENCE = 0;
142   
143    /**
144    * Creates a new TreePanel object.
145    *
146    * @param ap
147    * @param type
148    * @param modelName
149    * @param options
150    */
 
151  1 toggle public TreePanel(AlignmentPanel ap, String type, String modelName,
152    SimilarityParamsI options)
153    {
154  1 super();
155  1 this.setFrameIcon(null);
156  1 this.similarityParams = options;
157  1 initTreePanel(ap, type, modelName, null, null, null, null);
158   
159    // We know this tree has distances. JBPNote TODO: prolly should add this as
160    // a userdefined default
161    // showDistances(true);
162    }
163   
 
164  14 toggle public TreePanel(AlignmentPanel alignPanel, NewickFile newtree,
165    String theTitle, AlignmentView inputData)
166    {
167  14 this(alignPanel, newtree, theTitle, inputData, null, null);
168    }
169   
170    /**
171    * when true, leaf labels are annotations
172    */
173    boolean forAnnotation = false;
174   
 
175  14 toggle public TreePanel(AlignmentPanel alignPanel, NewickFile newtree,
176    String theTitle, AlignmentView inputData,
177    AlignmentAnnotation[] leafAnnotations, String subTitle)
178    {
179  14 super();
180  14 this.forAnnotation = leafAnnotations != null
181    && leafAnnotations.length > 0;
182  14 this.setFrameIcon(null);
183  14 this.treeTitle = theTitle;
184  14 initTreePanel(alignPanel, null, null, newtree, inputData, null, null);
185    }
186   
187    /**
188    * columnwise tree associated with positions in aa
189    *
190    * @param alignPanel
191    * @param fin
192    * @param title
193    * @param aa
194    */
 
195  0 toggle public TreePanel(AlignmentPanel alignPanel, NewickFile fin,
196    AlignmentAnnotation aa, String title)
197    {
198  0 super();
199  0 columnWise = true;
200  0 assocAnnotation = aa;
201  0 this.setFrameIcon(null);
202  0 this.treeTitle = title;
203  0 initTreePanel(alignPanel, null, null, fin, null, null, null);
204    }
205   
 
206  3 toggle public TreePanel(AlignmentPanel alignPanel, NewickFile newtree,
207    String theTitle, AlignmentView inputData,
208    AlignmentAnnotation[] leafAnnotations, String subTitle,
209    Map<String, AlignmentAnnotation> annotationIds)
210    {
211  3 super();
212  3 this.forAnnotation = leafAnnotations != null
213    && leafAnnotations.length > 0;
214  3 this.setFrameIcon(null);
215  3 this.treeTitle = theTitle;
216  3 initTreePanel(alignPanel, null, null, newtree, inputData, null, annotationIds);
217    }
218   
219   
220    boolean columnWise = false;
221   
222    AlignmentAnnotation assocAnnotation = null;
223   
 
224  1411 toggle public boolean isColumnWise()
225    {
226  1411 return columnWise;
227    }
228   
 
229  34 toggle public boolean isAnnotationBased()
230    {
231  34 return annotationBased;
232    }
233   
 
234  34 toggle public void setAnnotationBased(boolean annotationBased)
235    {
236  34 this.annotationBased = annotationBased;
237    }
238   
 
239  18 toggle public AlignmentAnnotation getAssocAnnotation()
240    {
241  18 return assocAnnotation;
242    }
243   
 
244  0 toggle public AlignmentI getAlignment()
245    {
246  0 return getTreeCanvas().getViewport().getAlignment();
247    }
248   
 
249  0 toggle public AlignmentViewport getViewPort()
250    {
251    // @Mungo - Why don't we return our own viewport ???
252  0 return getTreeCanvas().getViewport();
253    }
254   
255   
 
256  735 toggle public Map<String, Color> getSecondaryStructureProviderColorMap()
257    {
258  735 return secondaryStructureProviderColorMap;
259    }
260   
261    /**This method restore the colour map from the loaded project file.
262    * It replaces the current colour for the labels present in the
263    * loaded project file.
264    * @param colorMap colour map from the loaded project
265    */
 
266  3 toggle public void restoreSecondaryStructureProviderColorMap(Map<String, Color> colorMap)
267    {
268  3 secondaryStructureProviderColorMap = colorMap;
269  3 ColorUtils.restoreMyHSBSpacedColours(colorMap);
270    }
271   
 
272  18 toggle void initTreePanel(AlignmentPanel ap, String type, String modelName,
273    NewickFile newTree, AlignmentView inputData,
274    AlignmentAnnotation[] leafAnnotations, Map<String, AlignmentAnnotation> annotationIds)
275    {
276   
277  18 av = ap.av;
278  18 this.treeType = type;
279  18 this.scoreModelName = modelName;
280   
281  18 treeCanvas = new TreeCanvas(this, ap, scrollPane);
282  18 scrollPane.setViewportView(treeCanvas);
283   
284  18 if (isAnnotationBased() || (similarityParams != null
285    && similarityParams.getSecondaryStructureSource() != null))
286    {
287    //setting showSecondaryStructureProviderMenu to true if the
288    //similarity is based on secondary structure
289  1 showSecondaryStructureProviderMenu.setVisible(true);
290  1 sortAnnotationAssocViews.setVisible(true);
291  1 setAnnotationBased(true);
292    }
293    else {
294    //setting showSecondaryStructureProviderMenu to false if the
295    //similarity is not based on secondary structure
296  17 showSecondaryStructureProviderMenu.setVisible(false);
297  17 sortAnnotationAssocViews.setVisible(false);
298  17 setAnnotationBased(false);
299    }
300  18 if (leafAnnotations != null)
301    {
302  0 forAnnotation = true;
303    }
304  18 if (columnWise)
305    {
306  0 bootstrapMenu.setVisible(false);
307  0 placeholdersMenu.setState(false);
308  0 placeholdersMenu.setVisible(false);
309  0 fitToWindow.setState(false);
310  0 sortAssocViews.setVisible(false);
311  0 sortAnnotationAssocViews.setVisible(false);
312    }
313   
314  18 addKeyListener(new KeyAdapter()
315    {
 
316  0 toggle @Override
317    public void keyPressed(KeyEvent e)
318    {
319  0 switch (e.getKeyCode())
320    {
321  0 case 27: // escape
322  0 treeCanvas.clearSelectedLeaves();
323  0 e.consume();
324  0 break;
325   
326    }
327   
328    }
329    });
330  18 PaintRefresher.Register(this, ap.av.getSequenceSetId());
331   
332  18 buildAssociatedViewMenu();
333   
334  18 final PropertyChangeListener listener = addAlignmentListener();
335   
336    /*
337    * remove listener when window is closed, so that this
338    * panel can be garbage collected
339    */
340  18 addInternalFrameListener(new InternalFrameAdapter()
341    {
 
342  16 toggle @Override
343    public void internalFrameClosed(InternalFrameEvent evt)
344    {
345  16 if (av != null)
346    {
347  16 av.removePropertyChangeListener(listener);
348    }
349  16 releaseReferences();
350    }
351    });
352   
353  18 TreeLoader tl = new TreeLoader(newTree, inputData, leafAnnotations, annotationIds);
354  18 tl.start();
355   
356    }
357   
358   
359    /**
360    * This method adds the secondary structure provider panel. The panel includes
361    * scrollable list of secondary structure providers and their corresponding
362    * toggle buttons with color key. Secondary structure providers toggle buttons
363    * with color key is shown if user selects the option available in
364    * View->Show secondary structure providers -> As coloured lines. And, list of
365    * secondary structure providers in all other cases. A CardLayout is used to
366    * switch between views.
367    */
 
368  5 toggle public void addSSProviderPanel()
369    {
370   
371  5 if(secondaryStructureProviderColorMap == null || secondaryStructureProviderColorMap.isEmpty()) {
372  1 return;
373    }
374   
375    // Panels to display the color map and provider list
376  4 JPanel ssProviderColorMapPanel = new JPanel();
377  4 JScrollPane colorMapScrollPanel;
378   
379    // Set layouts to arrange components horizontally
380  4 ssProviderColorMapPanel.setLayout(new BoxLayout(ssProviderColorMapPanel, BoxLayout.X_AXIS));
381   
382    // Get the color mapping
383  4 List<Map.Entry<String, Color>> sortedSSProviderColorMap = new ArrayList<>(
384    secondaryStructureProviderColorMap.entrySet());
385   
386    // Sort the providers alphabetically by name
387  4 sortedSSProviderColorMap.sort(Map.Entry.comparingByKey());
388   
389    // Add toggle buttons and provider labels in the panels iteratively for each provider
390  4 for (Map.Entry<String, Color> sortedSSProviderColor : sortedSSProviderColorMap)
391    {
392    // Add toggle button panel
393  14 JPanel toggleButtonPanel = createToggleButtonForProvider(sortedSSProviderColor);
394  14 ssProviderColorMapPanel.add(toggleButtonPanel);
395  14 ssProviderColorMapPanel.add(Box.createRigidArea(new Dimension(GAP_WIDTH, 0))); // gap
396    }
397   
398    // Set an empty border for the color map panel with no padding
399  4 ssProviderColorMapPanel.setBorder(BorderFactory.createEmptyBorder(NO_PADDING,
400    NO_PADDING, NO_PADDING, NO_PADDING)); // no padding
401  4 ssProviderColorMapPanel.revalidate();
402  4 ssProviderColorMapPanel.repaint();
403   
404    // Add panels in scroll pane
405  4 colorMapScrollPanel = initializeColorMapScrollPanel(ssProviderColorMapPanel);
406   
407    // Create a CardLayout panel to switch between the panels
408  4 ssProviderPanel = new JPanel(new CardLayout());
409  4 ssProviderPanel.add(colorMapScrollPanel, "colorMapScrollPanel");
410   
411    // Show the panel based on user selection
412  4 CardLayout ssProviderCardLayout = (CardLayout) (ssProviderPanel.getLayout());
413  4 if (showStructureProviderColouredLines.isSelected()) {
414  0 ssProviderCardLayout.show(ssProviderPanel, "colorMapScrollPanel");
415  0 ssProviderPanel.setVisible(true);
416    }
417    else {
418  4 ssProviderPanel.setVisible(false);
419    }
420   
421  4 ssProviderPanel.repaint();
422   
423  4 this.add(ssProviderPanel, BorderLayout.NORTH);
424  4 this.revalidate();
425  4 this.repaint();
426    }
427   
428    /**
429    * Creates a toggle button panel for a secondary structure provider.
430    *
431    * @param colorKey A Map.Entry with the provider name and its colour.
432    * @return A JPanel with the toggle button.
433    */
 
434  14 toggle private JPanel createToggleButtonForProvider(Map.Entry<String, Color> colorKey)
435    {
436  14 String provider = colorKey.getKey();
437  14 Color color = colorKey.getValue();
438   
439  14 JToggleButton toggleButton = createToggleButton(provider, color);
440  14 JPanel toggleButtonPanel = new JPanel();
441  14 toggleButtonPanel.setLayout(new BoxLayout(toggleButtonPanel, BoxLayout.X_AXIS));
442  14 toggleButtonPanel.add(toggleButton);
443  14 toggleButtonPanel.add(Box.createRigidArea(new Dimension(GAP_WIDTH, 0))); // gap
444   
445  14 return toggleButtonPanel;
446    }
447   
448   
449    /**
450    * Creates a toggle button for a secondary structure provider with
451    * corresponding coloured icon. The secondary structure provider lines
452    * are coloured if the button is OFF (default). The lines are grayed
453    * if the button is ON.
454    *
455    * @param provider The name of the secondary structure provider.
456    * @param color The colour mapped with the secondary structure provider.
457    * @return JToggleButton with the secondary structure provider name
458    * and colour icon.
459    */
 
460  14 toggle private JToggleButton createToggleButton(String provider, Color color)
461    {
462  14 JToggleButton ssProviderToggleButton = new JToggleButton(provider);
463  14 ssProviderToggleButton.setFont(new Font(getTreeFont().getName(),
464    getTreeFont().getStyle(), SS_PROVIDER_PANEL_FONT_SIZE));
465  14 ssProviderToggleButton.setMargin(
466    new Insets(TOGGLE_BUTTON_MARGIN, TOGGLE_BUTTON_MARGIN,
467    TOGGLE_BUTTON_MARGIN, TOGGLE_BUTTON_MARGIN));
468   
469    // Icon to display the secondary structure provider colour
470  14 Icon toggleButtonColorIcon = new Icon()
471    {
 
472  44 toggle @Override
473    public void paintIcon(Component c, Graphics g, int x, int y)
474    {
475  44 g.setColor(color);
476  44 g.fillRect(x, y, getIconWidth(), getIconHeight());
477    }
478   
 
479  172 toggle @Override
480    public int getIconWidth()
481    {
482  172 return TOGGLE_BUTTON_ICON_WIDTH;
483    }
484   
 
485  172 toggle @Override
486    public int getIconHeight()
487    {
488  172 return TOGGLE_BUTTON_ICON_HEIGHT;
489    }
490    };
491   
492  14 ssProviderToggleButton.setIcon(toggleButtonColorIcon);
493  14 ssProviderToggleButton
494    .setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
495  14 ssProviderToggleButton.setIconTextGap(GAP_WIDTH);
496  14 ssProviderToggleButton.setSelected(false); // Set default to OFF
497   
498    // button listener
499  14 ssProviderToggleButton.addItemListener(e -> {
500  0 boolean selected = (e.getStateChange() == ItemEvent.SELECTED);
501  0 toggleStructureProviderColouredLine(provider, selected);
502    });
503   
504  14 return ssProviderToggleButton;
505    }
506   
507    /**
508    * Initializes a scroll panel to wrap the given JPanel to include in the tree panel.
509    *
510    * @param jPanel The JPanel to wrap in a scroll pane.
511    * @return A JScrollPane containing the given JPanel.
512    */
 
513  4 toggle private JScrollPane initializeColorMapScrollPanel(JPanel jPanel)
514    {
515  4 JScrollPane scrollPanel = new JScrollPane(jPanel);
516  4 scrollPanel.setPreferredSize(new Dimension(NO_WIDTH_PREFERENCE, SCROLL_PANEL_HEIGHT));
517  4 scrollPanel.setHorizontalScrollBarPolicy(
518    JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
519   
520  4 JScrollBar horizontalScrollBar = scrollPanel
521    .getHorizontalScrollBar();
522  4 horizontalScrollBar.setPreferredSize(
523    new Dimension(NO_WIDTH_PREFERENCE, SCROLLBAR_HEIGHT));
524   
525  4 return scrollPanel;
526    }
527   
528   
529   
530    /**
531    * Ensure any potentially large object references are nulled
532    */
 
533  16 toggle public void releaseReferences()
534    {
535  16 this.tree = null;
536  16 this.treeCanvas.tree = null;
537  16 this.treeCanvas.nodeHash = null;
538  16 this.treeCanvas.nameHash = null;
539    }
540   
541    /**
542    * @return
543    */
 
544  18 toggle protected PropertyChangeListener addAlignmentListener()
545    {
546  18 final PropertyChangeListener listener = new PropertyChangeListener()
547    {
 
548  0 toggle @SuppressWarnings("unchecked")
549    @Override
550    public void propertyChange(PropertyChangeEvent evt)
551    {
552  0 switch (evt.getPropertyName()) {
553  0 case AlignmentViewport.PROPERTY_ALIGNMENT:
554  0 if (tree == null)
555    {
556  0 jalview.bin.Console.outPrintln("tree is null");
557    // TODO: deal with case when a change event is received whilst a
558    // tree is still being calculated - should save reference for
559    // processing message later.
560  0 return;
561    }
562  0 if (evt.getNewValue() == null)
563    {
564  0 jalview.bin.Console.outPrintln(
565    "new alignment sequences vector value is null");
566  0 return;
567    }
568   
569    // TODO: JAL-4107 Alignment notify property changed: in 2.11.5 we get AlignmentI not List<SequenceI>
570  0 tree.updatePlaceHolders((List<SequenceI>) evt.getNewValue());
571  0 treeCanvas.nameHash.clear(); // reset the mapping between canvas
572    // rectangles and leafnodes
573  0 repaint();
574  0 break;
575    }
576    }
577    };
578  18 av.addPropertyChangeListener(listener);
579  18 return listener;
580    }
581   
 
582  0 toggle @Override
583    public void viewMenu_menuSelected()
584    {
585  0 buildAssociatedViewMenu();
586    }
587   
 
588  18 toggle void buildAssociatedViewMenu()
589    {
590  18 AlignmentPanel[] aps = PaintRefresher
591    .getAssociatedPanels(av.getSequenceSetId());
592  18 if (aps.length == 1 && getTreeCanvas().getAssociatedPanel() == aps[0])
593    {
594  10 associateLeavesMenu.setVisible(false);
595  10 return;
596    }
597   
598  8 associateLeavesMenu.setVisible(true);
599   
600  8 if ((viewMenu
601    .getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
602    {
603  8 viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
604    }
605   
606  8 associateLeavesMenu.removeAll();
607   
608  8 JRadioButtonMenuItem item;
609  8 ButtonGroup buttonGroup = new ButtonGroup();
610  8 int i, iSize = aps.length;
611  8 final TreePanel thisTreePanel = this;
612  36 for (i = 0; i < iSize; i++)
613    {
614  28 final AlignmentPanel ap = aps[i];
615  28 item = new JRadioButtonMenuItem(ap.av.getViewName(),
616    ap == treeCanvas.getAssociatedPanel());
617  28 buttonGroup.add(item);
618  28 item.addActionListener(new ActionListener()
619    {
 
620  0 toggle @Override
621    public void actionPerformed(ActionEvent evt)
622    {
623  0 treeCanvas.applyToAllViews = false;
624  0 treeCanvas.setAssociatedPanel(ap);
625  0 treeCanvas.setViewport(ap.av);
626  0 PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId());
627    }
628    });
629   
630  28 associateLeavesMenu.add(item);
631    }
632   
633  8 final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
634    MessageManager.getString("label.all_views"));
635  8 buttonGroup.add(itemf);
636  8 itemf.setSelected(treeCanvas.applyToAllViews);
637  8 itemf.addActionListener(new ActionListener()
638    {
 
639  0 toggle @Override
640    public void actionPerformed(ActionEvent evt)
641    {
642  0 treeCanvas.applyToAllViews = itemf.isSelected();
643    }
644    });
645  8 associateLeavesMenu.add(itemf);
646   
647    }
648   
 
649    class TreeLoader extends Thread
650    {
651    private NewickFile newtree;
652   
653    private AlignmentView odata = null;
654   
655    private AlignmentAnnotation[] leafAnnotations;
656   
657    private Map<String, AlignmentAnnotation> annotationIds = null;
658   
 
659  18 toggle public TreeLoader(NewickFile newickFile, AlignmentView inputData,
660    AlignmentAnnotation[] leafAnnotations,
661    Map<String, AlignmentAnnotation> annotationIds)
662    {
663  18 this.newtree = newickFile;
664  18 this.odata = inputData;
665  18 this.leafAnnotations = leafAnnotations;
666  18 this.annotationIds = annotationIds;
667   
668  18 if (newickFile != null)
669    {
670    // Must be outside run(), as Jalview2XML tries to
671    // update distance/bootstrap visibility at the same time
672  17 showBootstrap(newickFile.HasBootstrap());
673  17 showDistances(newickFile.HasDistances());
674    }
675    }
676   
 
677  18 toggle @Override
678    public void run()
679    {
680   
681  18 if (newtree != null)
682    {
683  17 tree = new TreeModel(av.getAlignment().getSequencesArray(), odata,
684    newtree, leafAnnotations);
685  17 if (tree.getOriginalData() == null)
686    {
687  17 originalSeqData.setVisible(false);
688    }
689    }
690    else
691    {
692  1 treeTitle=null;
693  1 ScoreModelI sm = ScoreModels.getInstance().getScoreModel(
694    scoreModelName, treeCanvas.getAssociatedPanel());
695  1 scoreModelName = sm.getDescription();
696   
697   
698  1 TreeBuilder njtree = treeType.equals(TreeBuilder.NEIGHBOUR_JOINING)
699    ? new NJTree(av, sm, similarityParams)
700    : new AverageDistanceTree(av, sm, similarityParams);
701  1 List<String> labels = njtree.getLabels();
702  1 if(labels != null && labels.size()>0) {
703  1 secondaryStructureProviderColorMap = AlignmentUtils.assignColorsForSecondaryStructureProviders(labels);
704  1 if(secondaryStructureProviderColorMap.size()>0) {
705  1 addSSProviderPanel();
706    }
707    }
708  1 tree = new TreeModel(njtree);
709   
710    // don't display distances for columnwise trees
711   
712    }
713   
714    // Process tree nodes to map annotations if annotation ids are present
715  18 if (annotationIds != null && annotationIds.size() > 0)
716    {
717  3 TreePanel.processTreeNodes(tree, annotationIds);
718    }
719   
720  18 showDistances(!columnWise);
721  18 tree.reCount(tree.getTopNode());
722  18 tree.findHeight(tree.getTopNode());
723  18 treeCanvas.setTree(tree);
724  18 treeCanvas.repaint();
725  18 av.setCurrentTree(tree);
726  18 if (av.getSortByTree())
727    {
728  1 sortByTree_actionPerformed();
729    }
730    }
731    }
732   
 
733  38 toggle public void showDistances(boolean b)
734    {
735  38 treeCanvas.setShowDistances(b);
736  38 distanceMenu.setSelected(b);
737    }
738   
 
739  20 toggle public void showBootstrap(boolean b)
740    {
741  20 treeCanvas.setShowBootstrap(b);
742  20 bootstrapMenu.setSelected(b);
743    }
744   
 
745  3 toggle public void showPlaceholders(boolean b)
746    {
747  3 placeholdersMenu.setState(b);
748  3 treeCanvas.setMarkPlaceholders(b);
749    }
750   
751    /**
752    * DOCUMENT ME!
753    *
754    * @return DOCUMENT ME!
755    */
 
756  19 toggle public TreeModel getTree()
757    {
758  19 return tree;
759    }
760   
761    /**
762    * DOCUMENT ME!
763    *
764    * @param e
765    * DOCUMENT ME!
766    */
 
767  0 toggle @Override
768    public void textbox_actionPerformed(ActionEvent e)
769    {
770  0 CutAndPasteTransfer cap = new CutAndPasteTransfer();
771   
772  0 String newTitle = getPanelTitle();
773   
774  0 NewickFile fout = new NewickFile(tree.getTopNode());
775  0 try
776    {
777  0 cap.setText(fout.print(tree.hasBootstrap(), tree.hasDistances(),
778    tree.hasRootDistance()));
779  0 Desktop.addInternalFrame(cap, newTitle, 500, 100);
780    } catch (OutOfMemoryError oom)
781    {
782  0 new OOMWarning("generating newick tree file", oom);
783  0 cap.dispose();
784    }
785   
786    }
787   
788    /**
789    * DOCUMENT ME!
790    *
791    * @param e
792    * DOCUMENT ME!
793    */
 
794  0 toggle @Override
795    public void saveAsNewick_actionPerformed(ActionEvent e)
796    {
797    // TODO: JAL-3048 save newick file for Jalview-JS
798  0 JalviewFileChooser chooser = new JalviewFileChooser(
799    Cache.getProperty("LAST_DIRECTORY"));
800  0 chooser.setFileView(new JalviewFileView());
801  0 chooser.setDialogTitle(
802    MessageManager.getString("label.save_tree_as_newick"));
803  0 chooser.setToolTipText(MessageManager.getString("action.save"));
804   
805  0 int value = chooser.showSaveDialog(null);
806   
807  0 if (value == JalviewFileChooser.APPROVE_OPTION)
808    {
809  0 String choice = chooser.getSelectedFile().getPath();
810  0 Cache.setProperty("LAST_DIRECTORY",
811    chooser.getSelectedFile().getParent());
812   
813  0 try
814    {
815  0 jalview.io.NewickFile fout = new jalview.io.NewickFile(
816    tree.getTopNode());
817  0 String output = fout.print(tree.hasBootstrap(), tree.hasDistances(),
818    tree.hasRootDistance());
819  0 java.io.PrintWriter out = new java.io.PrintWriter(
820    new java.io.FileWriter(choice));
821  0 out.println(output);
822  0 out.close();
823    } catch (Exception ex)
824    {
825  0 ex.printStackTrace();
826    }
827    }
828    }
829   
830    /**
831    * DOCUMENT ME!
832    *
833    * @param e
834    * DOCUMENT ME!
835    */
 
836  0 toggle @Override
837    public void printMenu_actionPerformed(ActionEvent e)
838    {
839    // Putting in a thread avoids Swing painting problems
840  0 treeCanvas.startPrinting();
841    }
842   
 
843  0 toggle @Override
844    public void originalSeqData_actionPerformed(ActionEvent e)
845    {
846  0 AlignmentView originalData = tree.getOriginalData();
847  0 if (originalData == null)
848    {
849  0 Console.info(
850    "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
851  0 return;
852    }
853    // decide if av alignment is sufficiently different to original data to
854    // warrant a new window to be created
855    // create new alignmnt window with hidden regions (unhiding hidden regions
856    // yields unaligned seqs)
857    // or create a selection box around columns in alignment view
858    // test Alignment(SeqCigar[])
859  0 char gc = '-';
860  0 try
861    {
862    // we try to get the associated view's gap character
863    // but this may fail if the view was closed...
864  0 gc = av.getGapCharacter();
865   
866    } catch (Exception ex)
867    {
868    }
869   
870  0 Object[] alAndColsel = originalData.getAlignmentAndHiddenColumns(gc);
871   
872  0 if (alAndColsel != null && alAndColsel[0] != null)
873    {
874    // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
875   
876  0 AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
877  0 AlignmentI dataset = (av != null && av.getAlignment() != null)
878    ? av.getAlignment().getDataset()
879    : null;
880  0 if (dataset != null)
881    {
882  0 al.setDataset(dataset);
883    }
884   
885  0 if (true)
886    {
887    // make a new frame!
888  0 AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1],
889    AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
890   
891    // >>>This is a fix for the moment, until a better solution is
892    // found!!<<<
893    // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
894   
895    // af.addSortByOrderMenuItem(ServiceName + " Ordering",
896    // msaorder);
897   
898  0 Desktop.addInternalFrame(af, MessageManager.formatMessage(
899    "label.original_data_for_params", new Object[]
900    { this.title }), AlignFrame.DEFAULT_WIDTH,
901    AlignFrame.DEFAULT_HEIGHT);
902    }
903    }
904    }
905   
906    /**
907    * DOCUMENT ME!
908    *
909    * @param e
910    * DOCUMENT ME!
911    */
 
912  3 toggle @Override
913    public void fitToWindow_actionPerformed(ActionEvent e)
914    {
915  3 treeCanvas.fitToWindow = fitToWindow.isSelected();
916  3 repaint();
917    }
918   
919    /** This method restores the menu options and view settings
920    * in the tree panel if the tree is based on annotation.
921    *
922    * @param viewName The type of annotation view in the loaded project
923    * : None, Labels, Coloured Lines.
924    */
 
925  4 toggle public void setShowAnnotationAs(String viewName)
926    {
927   
928  4 if (isAnnotationBased())
929    {
930    //setting showSecondaryStructureProviderMenu to true if the
931    //similarity is based on secondary structure
932  4 addSSProviderPanel();
933  4 showSecondaryStructureProviderMenu.setVisible(true);
934  4 setShowAnnotationAsView(viewName);
935  4 sortAnnotationAssocViews.setVisible(true);
936   
937    }
938  4 this.treeCanvas.revalidate();
939  4 revalidate();
940  4 repaint();
941    }
942   
943    /** This method restore the annotation view based on the view name
944    *
945    * @param viewName Name of the view name (for None, Labels, Coloured Lines)
946    * which matches with the text displayed in the button.
947    */
 
948  4 toggle public void setShowAnnotationAsView(String viewName)
949    {
950  4 for (AbstractButton button : Arrays.asList(showStructureProviderLabels,
951    showStructureProviderColouredLines, hideStructureProviders)) {
952  8 if (button.getText().equals(viewName)) {
953  4 button.doClick(); // Triggers the action event
954  4 return;
955    }
956    }
957    // The default view - None
958  0 hideStructureProviders.doClick();
959    }
960   
961    /**
962    * This method retrieves the text in the selected view button (for None,
963    * Labels, Coloured Lines) in the tree panel and returns it.
964    * @return The text in the selected view button
965    */
 
966  4 toggle public String getShowAnnotationAs() {
967  4 for (AbstractButton button : Arrays.asList(showStructureProviderLabels, showStructureProviderColouredLines, hideStructureProviders)) {
968  8 if (button.isSelected()) {
969  4 return button.getText(); // Gets the text
970    }
971    }
972  0 return hideStructureProviders.getText();
973    }
974   
975   
976    /**
977    * sort the associated alignment view by the current tree.
978    *
979    * @param e
980    */
 
981  1 toggle @Override
982    public void sortByTree_actionPerformed()
983    {
984   
985  1 if (treeCanvas.applyToAllViews)
986    {
987  0 final ArrayList<CommandI> commands = new ArrayList<>();
988  0 for (AlignmentPanel ap : PaintRefresher
989    .getAssociatedPanels(av.getSequenceSetId()))
990    {
991  0 commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
992    }
993  0 av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
994    {
995   
 
996  0 toggle @Override
997    public void undoCommand(AlignmentI[] views)
998    {
999  0 for (CommandI tsort : commands)
1000    {
1001  0 tsort.undoCommand(views);
1002    }
1003    }
1004   
 
1005  0 toggle @Override
1006    public int getSize()
1007    {
1008  0 return commands.size();
1009    }
1010   
 
1011  0 toggle @Override
1012    public String getDescription()
1013    {
1014  0 return "Tree Sort (many views)";
1015    }
1016   
 
1017  0 toggle @Override
1018    public void doCommand(AlignmentI[] views)
1019    {
1020   
1021  0 for (CommandI tsort : commands)
1022    {
1023  0 tsort.doCommand(views);
1024    }
1025    }
1026    });
1027  0 for (AlignmentPanel ap : PaintRefresher
1028    .getAssociatedPanels(av.getSequenceSetId()))
1029    {
1030    // ensure all the alignFrames refresh their GI after adding an undo item
1031  0 ap.alignFrame.updateEditMenuBar();
1032    }
1033    }
1034    else
1035    {
1036  1 treeCanvas.getAssociatedPanel().alignFrame.addHistoryItem(
1037    sortAlignmentIn(treeCanvas.getAssociatedPanel()));
1038    }
1039   
1040    }
1041   
1042    /**
1043    * This method sort the annotation panel by tree. The sorted annotation rows
1044    * from tree appear first followed by other annotation rows in the original
1045    * order. Sort annotation by sequence/label overrides sort annotation by tree.
1046    */
 
1047  0 toggle @Override
1048    public void sortAnnotationByTree_actionPerformed()
1049    {
1050  0 if (getAlignment() != null
1051    && getAlignment().getAlignmentAnnotation() != null
1052    && getAlignment().getAlignmentAnnotation().length > 0)
1053    {
1054    // Initialize annotation sorter with the alignment
1055  0 final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
1056    av.isShowAutocalculatedAbove());
1057   
1058    // Sort by tree
1059  0 sorter.sortByTree(getAlignment().getAlignmentAnnotation(), tree);
1060  0 av.setSortAnnotationsBy(SequenceAnnotationOrder.NONE);
1061    // Repaint the alignment panel after the sort
1062  0 av.getAlignPanel().repaint();
1063    }
1064    }
1065   
1066   
 
1067  1 toggle public CommandI sortAlignmentIn(AlignmentPanel ap)
1068    {
1069    // TODO: move to alignment view controller
1070  1 AlignmentViewport viewport = ap.av;
1071  1 SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
1072  1 AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
1073  1 CommandI undo;
1074  1 undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment());
1075   
1076  1 ap.paintAlignment(true, false);
1077  1 return undo;
1078    }
1079   
1080    /**
1081    * DOCUMENT ME!
1082    *
1083    * @param e
1084    * DOCUMENT ME!
1085    */
 
1086  0 toggle @Override
1087    public void font_actionPerformed(ActionEvent e)
1088    {
1089  0 if (treeCanvas == null)
1090    {
1091  0 return;
1092    }
1093   
1094  0 new FontChooser(this);
1095    }
1096   
 
1097  46 toggle public Font getTreeFont()
1098    {
1099  46 return treeCanvas.font;
1100    }
1101   
 
1102  3 toggle public void setTreeFont(Font f)
1103    {
1104  3 if (treeCanvas != null)
1105    {
1106  3 treeCanvas.setFont(f);
1107    }
1108    }
1109   
1110    /**
1111    * DOCUMENT ME!
1112    *
1113    * @param e
1114    * DOCUMENT ME!
1115    */
 
1116  0 toggle @Override
1117    public void distanceMenu_actionPerformed(ActionEvent e)
1118    {
1119  0 treeCanvas.setShowDistances(distanceMenu.isSelected());
1120    }
1121   
 
1122  0 toggle @Override
1123    public void hideStructureProviders_actionPerformed(ActionEvent e)
1124    {
1125  0 treeCanvas.hideStructureProviders(hideStructureProviders.isSelected());
1126   
1127    // Show the panel based on user selection
1128  0 CardLayout ssProviderCardLayout = (CardLayout) (ssProviderPanel.getLayout());
1129  0 if (showStructureProviderColouredLines.isSelected()) {
1130  0 ssProviderCardLayout.show(ssProviderPanel, "colorMapScrollPanel");
1131  0 ssProviderPanel.setVisible(true);
1132    }
1133    else {
1134  0 ssProviderPanel.setVisible(false);
1135    }
1136  0 ssProviderPanel.repaint();
1137    }
1138   
 
1139  4 toggle @Override
1140    public void showStructureProviderColouredLines_actionPerformed(ActionEvent e)
1141    {
1142  4 treeCanvas.setShowStructureProviderColouredLines(showStructureProviderColouredLines.isSelected());
1143   
1144    // Show the panel based on user selection
1145  4 CardLayout ssProviderCardLayout = (CardLayout) (ssProviderPanel.getLayout());
1146   
1147  4 if (showStructureProviderColouredLines.isSelected()) {
1148  4 ssProviderCardLayout.show(ssProviderPanel, "colorMapScrollPanel");
1149  4 ssProviderPanel.setVisible(true);
1150    }
1151    else {
1152  0 ssProviderPanel.setVisible(false);
1153    }
1154  4 ssProviderPanel.repaint();
1155    }
1156   
 
1157  0 toggle @Override
1158    public void showStructureProviderLabels_actionPerformed(ActionEvent e)
1159    {
1160  0 treeCanvas.setShowStructureProviderLabels(showStructureProviderLabels.isSelected());
1161   
1162    // Show the panel based on user selection
1163  0 CardLayout ssProviderCardLayout = (CardLayout) (ssProviderPanel.getLayout());
1164  0 if (showStructureProviderColouredLines.isSelected()) {
1165  0 ssProviderCardLayout.show(ssProviderPanel, "colorMapScrollPanel");
1166  0 ssProviderPanel.setVisible(true);
1167    }
1168    else {
1169  0 ssProviderPanel.setVisible(false);
1170    }
1171  0 ssProviderPanel.repaint();
1172    }
1173   
1174   
 
1175  0 toggle public void toggleStructureProviderColouredLine(String provider, boolean action)
1176    {
1177  0 treeCanvas.toggleStructureProviderColouredLine(provider, action);
1178    }
1179   
1180    /**
1181    * DOCUMENT ME!
1182    *
1183    * @param e
1184    * DOCUMENT ME!
1185    */
 
1186  0 toggle @Override
1187    public void bootstrapMenu_actionPerformed(ActionEvent e)
1188    {
1189  0 treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
1190    }
1191   
1192    /**
1193    * DOCUMENT ME!
1194    *
1195    * @param e
1196    * DOCUMENT ME!
1197    */
 
1198  0 toggle @Override
1199    public void placeholdersMenu_actionPerformed(ActionEvent e)
1200    {
1201  0 treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
1202    }
1203   
1204    /**
1205    * Outputs the Tree in image format (currently EPS or PNG). The user is
1206    * prompted for the file to save to, and for EPS (unless a preference is
1207    * already set) for the choice of Text or Lineart for character rendering.
1208    */
 
1209  0 toggle @Override
1210    public void writeTreeImage(TYPE imageFormat)
1211    {
1212  0 int width = treeCanvas.getWidth();
1213  0 int height = treeCanvas.getHeight();
1214  0 ImageWriterI writer = new ImageWriterI()
1215    {
 
1216  0 toggle @Override
1217    public void exportImage(Graphics g) throws Exception
1218    {
1219  0 treeCanvas.draw(g, width, height);
1220    }
1221    };
1222  0 String tree = MessageManager.getString("label.tree");
1223  0 ImageExporter exporter = new ImageExporter(writer, null, imageFormat,
1224    tree);
1225  0 try
1226    {
1227  0 exporter.doExport(null, this, width, height,
1228    tree.toLowerCase(Locale.ROOT));
1229    } catch (ImageOutputException ioex)
1230    {
1231  0 Console.error(
1232    "Unexpected error whilst writing " + imageFormat.toString(),
1233    ioex);
1234    }
1235    }
1236   
1237    /**
1238    * change node labels to the annotation referred to by labelClass TODO:
1239    * promote to a datamodel modification that can be undone TODO: make argument
1240    * one case of a generic transformation function ie { undoStep = apply(Tree,
1241    * TransformFunction)};
1242    *
1243    * @param labelClass
1244    */
 
1245  0 toggle public void changeNames(final String labelClass)
1246    {
1247  0 tree.applyToNodes(new NodeTransformI()
1248    {
1249   
 
1250  0 toggle @Override
1251    public void transform(BinaryNode node)
1252    {
1253  0 if (node instanceof SequenceNode
1254    && !((SequenceNode) node).isPlaceholder()
1255    && !((SequenceNode) node).isDummy())
1256    {
1257  0 String newname = null;
1258  0 SequenceI sq = (SequenceI) ((BinaryNode) node).element();
1259  0 if (sq != null)
1260    {
1261    // search dbrefs, features and annotation
1262  0 List<DBRefEntry> refs = jalview.util.DBRefUtils
1263    .selectRefs(sq.getDBRefs(), new String[]
1264    { labelClass.toUpperCase(Locale.ROOT) });
1265  0 if (refs != null)
1266    {
1267  0 for (int i = 0, ni = refs.size(); i < ni; i++)
1268    {
1269  0 if (newname == null)
1270    {
1271  0 newname = new String(refs.get(i).getAccessionId());
1272    }
1273    else
1274    {
1275  0 newname += "; " + refs.get(i).getAccessionId();
1276    }
1277    }
1278    }
1279  0 if (newname == null)
1280    {
1281  0 List<SequenceFeature> features = sq.getFeatures()
1282    .getPositionalFeatures(labelClass);
1283  0 for (SequenceFeature feature : features)
1284    {
1285  0 if (newname == null)
1286    {
1287  0 newname = feature.getDescription();
1288    }
1289    else
1290    {
1291  0 newname = newname + "; " + feature.getDescription();
1292    }
1293    }
1294    }
1295    }
1296  0 if (newname != null)
1297    {
1298    // String oldname = ((SequenceNode) node).getName();
1299    // TODO : save oldname in the undo object for this modification.
1300  0 ((BinaryNode) node).setName(newname);
1301    }
1302    }
1303    }
1304    });
1305    }
1306   
1307    /**
1308    * Formats a localised title for the tree panel, like
1309    * <p>
1310    * Neighbour Joining Using BLOSUM62
1311    * <p>
1312    * For a tree loaded from file, just uses the file name
1313    *
1314    * @return
1315    */
 
1316  0 toggle public String getPanelTitle()
1317    {
1318  0 if (treeTitle != null)
1319    {
1320  0 return treeTitle;
1321    }
1322   
1323    /*
1324    * i18n description of Neighbour Joining or Average Distance method
1325    */
1326  0 String treecalcnm = MessageManager.getString(
1327    "label.tree_calc_" + treeType.toLowerCase(Locale.ROOT));
1328   
1329    /*
1330    * short score model name (long description can be too long)
1331    */
1332  0 String smn = scoreModelName;
1333   
1334    /*
1335    * put them together as <method> Using <model>
1336    */
1337  0 final String ttl = MessageManager.formatMessage("label.calc_title",
1338    treecalcnm, smn);
1339  0 return ttl;
1340    }
1341   
1342    /**
1343    * Builds an EPS image and writes it to the specified file.
1344    *
1345    * @param outFile
1346    * @param textOption
1347    * true for Text character rendering, false for Lineart
1348    */
 
1349  0 toggle protected void writeEpsFile(File outFile, boolean textOption)
1350    {
1351  0 try
1352    {
1353  0 int width = treeCanvas.getWidth();
1354  0 int height = treeCanvas.getHeight();
1355   
1356  0 FileOutputStream out = new FileOutputStream(outFile);
1357  0 EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width,
1358    height);
1359  0 pg.setAccurateTextMode(!textOption);
1360  0 treeCanvas.draw(pg, width, height);
1361   
1362  0 pg.flush();
1363  0 pg.close();
1364    } catch (Exception ex)
1365    {
1366  0 jalview.bin.Console.errPrintln("Error writing tree as EPS");
1367  0 ex.printStackTrace();
1368    }
1369    }
1370   
 
1371  0 toggle public AlignViewport getViewport()
1372    {
1373  0 return av;
1374    }
1375   
 
1376  0 toggle public void setViewport(AlignViewport av)
1377    {
1378  0 this.av = av;
1379    }
1380   
 
1381  47 toggle public TreeCanvas getTreeCanvas()
1382    {
1383  47 return treeCanvas;
1384    }
1385   
1386    /** This method maps the tree nodes with annotation rows and respective sequences.
1387    *
1388    * @param alignPanel Alignment panel object
1389    * @param nf Loaded newick file
1390    * @param annotationIds Map of annotation ids and respective annotation rows
1391    * @param treeTitle Title of the tree panel
1392    * @param input Alignment view object
1393    * @return Returns tree panel with the nodes mapped with annotation rows and sequences
1394    */
 
1395  3 toggle public static TreePanel newTreeForAnnotations(AlignmentPanel alignPanel,
1396    NewickFile nf, Map<String, AlignmentAnnotation> annotationIds,
1397    String treeTitle, AlignmentView input)
1398    {
1399    // Create TreePanel instance
1400  3 TreePanel tp = new TreePanel(alignPanel, nf, treeTitle, input, null,
1401    "Annotation Tree", annotationIds);
1402  3 return tp;
1403    }
1404   
1405   
1406    /**
1407    * Processes tree nodes by applying transformations based on annotations.
1408    * Maps the nodes with annotation rows and/or sequences
1409    * @param tree
1410    * @param annotationIds
1411    */
 
1412  3 toggle private static void processTreeNodes(TreeModel tree,
1413    Map<String, AlignmentAnnotation> annotationIds)
1414    {
1415    // Initialise the node attribute in the tree model
1416    // object with the leaf nodes
1417  3 BinaryNode topNode = tree.getTopNode();
1418  3 Vector<BinaryNode> leafNodes = tree.findLeaves(topNode);
1419  3 tree.setNode(leafNodes);
1420  3 List<SequenceI> seqs = new ArrayList<SequenceI>();
1421    //Apply transformation: map nodes with annotation rows and sequences
1422  3 tree.applyToNodes(node -> {
1423  64 Object element = node.element();
1424  64 String nodeName = node.getName(); //Annotation id in case of annotation based tree
1425   
1426    // Extract annotation ID
1427  64 String annotId = nodeName.replaceAll("[^a-z_A-Z0-9]", "");
1428   
1429    //Get the annotation object
1430  64 AlignmentAnnotation annot = annotationIds.get(annotId);
1431   
1432  64 if (annot != null)
1433    {
1434    // Map the sequence and annotation with the node
1435  36 SequenceI seq = annot.sequenceRef;
1436    // TODO clean up the binary tree object inheritance to avoid casting
1437  36 node.setElement(seq);
1438  36 ((SequenceNode)node).setPlaceholder(false);
1439  36 node.setName(seq.getName());
1440   
1441    // Get annotation provider name from the annotation description
1442  36 String ssAnnotProvider = AlignmentAnnotationUtils
1443    .extractSSSourceFromAnnotationDescription(annot);
1444  36 node.setLabel(ssAnnotProvider);
1445  36 node.setAlignmentAnnotation(annot);
1446   
1447    // Initialise AnnotationDetails if present for the annotation
1448  36 if (annot.hasAnnotationDetailsProperty())
1449    {
1450  17 node.setAnnotationDetails(annot.getAnnotationDetailsProperty());
1451    }
1452   
1453    } else {
1454  28 if (Console.isTraceEnabled())
1455    {
1456  0 Console.trace("Annotation based tree: ignoring unmapped id "+annotId);
1457    }
1458   
1459  28 if (element instanceof SequenceI)
1460    {
1461    //If annotation is absent, set the default label ?
1462    // why ?
1463  28 node.setLabel(Constants.STRUCTURE_PROVIDERS.get("None"));
1464    }
1465    }
1466    });
1467    }
1468    }