Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 GMT
  2. Package jalview.gui

File TreePanel.java

 

Coverage histogram

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

Code metrics

94
395
72
2
1,463
986
138
0.35
5.49
36
1.92

Classes

Class Line # Actions
TreePanel 110 366 126
0.5058365550.6%
TreePanel.TreeLoader 649 29 12
0.914893691.5%
 

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  1173 toggle public boolean isColumnWise()
225    {
226  1173 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  658 toggle public Map<String, Color> getSecondaryStructureProviderColorMap()
257    {
258  658 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  36 toggle @Override
473    public void paintIcon(Component c, Graphics g, int x, int y)
474    {
475  36 g.setColor(color);
476  36 g.fillRect(x, y, getIconWidth(), getIconHeight());
477    }
478   
 
479  156 toggle @Override
480    public int getIconWidth()
481    {
482  156 return TOGGLE_BUTTON_ICON_WIDTH;
483    }
484   
 
485  156 toggle @Override
486    public int getIconHeight()
487    {
488  156 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  39 for (i = 0; i < iSize; i++)
613    {
614  31 final AlignmentPanel ap = aps[i];
615  31 item = new JRadioButtonMenuItem(ap.av.getViewName(),
616    ap == treeCanvas.getAssociatedPanel());
617  31 buttonGroup.add(item);
618  31 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  31 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 ScoreModelI sm = ScoreModels.getInstance().getScoreModel(
693    scoreModelName, treeCanvas.getAssociatedPanel());
694  1 TreeBuilder njtree = treeType.equals(TreeBuilder.NEIGHBOUR_JOINING)
695    ? new NJTree(av, sm, similarityParams)
696    : new AverageDistanceTree(av, sm, similarityParams);
697  1 List<String> labels = njtree.getLabels();
698  1 if(labels != null && labels.size()>0) {
699  1 secondaryStructureProviderColorMap = AlignmentUtils.assignColorsForSecondaryStructureProviders(labels);
700  1 if(secondaryStructureProviderColorMap.size()>0) {
701  1 addSSProviderPanel();
702    }
703    }
704  1 tree = new TreeModel(njtree);
705    // don't display distances for columnwise trees
706   
707    }
708   
709    // Process tree nodes to map annotations if annotation ids are present
710  18 if (annotationIds != null && annotationIds.size() > 0)
711    {
712  3 TreePanel.processTreeNodes(tree, annotationIds);
713    }
714   
715  18 showDistances(!columnWise);
716  18 tree.reCount(tree.getTopNode());
717  18 tree.findHeight(tree.getTopNode());
718  18 treeCanvas.setTree(tree);
719  18 treeCanvas.repaint();
720  18 av.setCurrentTree(tree);
721  18 if (av.getSortByTree())
722    {
723  1 sortByTree_actionPerformed();
724    }
725    }
726    }
727   
 
728  38 toggle public void showDistances(boolean b)
729    {
730  38 treeCanvas.setShowDistances(b);
731  38 distanceMenu.setSelected(b);
732    }
733   
 
734  20 toggle public void showBootstrap(boolean b)
735    {
736  20 treeCanvas.setShowBootstrap(b);
737  20 bootstrapMenu.setSelected(b);
738    }
739   
 
740  3 toggle public void showPlaceholders(boolean b)
741    {
742  3 placeholdersMenu.setState(b);
743  3 treeCanvas.setMarkPlaceholders(b);
744    }
745   
746    /**
747    * DOCUMENT ME!
748    *
749    * @return DOCUMENT ME!
750    */
 
751  19 toggle public TreeModel getTree()
752    {
753  19 return tree;
754    }
755   
756    /**
757    * DOCUMENT ME!
758    *
759    * @param e
760    * DOCUMENT ME!
761    */
 
762  0 toggle @Override
763    public void textbox_actionPerformed(ActionEvent e)
764    {
765  0 CutAndPasteTransfer cap = new CutAndPasteTransfer();
766   
767  0 String newTitle = getPanelTitle();
768   
769  0 NewickFile fout = new NewickFile(tree.getTopNode());
770  0 try
771    {
772  0 cap.setText(fout.print(tree.hasBootstrap(), tree.hasDistances(),
773    tree.hasRootDistance()));
774  0 Desktop.addInternalFrame(cap, newTitle, 500, 100);
775    } catch (OutOfMemoryError oom)
776    {
777  0 new OOMWarning("generating newick tree file", oom);
778  0 cap.dispose();
779    }
780   
781    }
782   
783    /**
784    * DOCUMENT ME!
785    *
786    * @param e
787    * DOCUMENT ME!
788    */
 
789  0 toggle @Override
790    public void saveAsNewick_actionPerformed(ActionEvent e)
791    {
792    // TODO: JAL-3048 save newick file for Jalview-JS
793  0 JalviewFileChooser chooser = new JalviewFileChooser(
794    Cache.getProperty("LAST_DIRECTORY"));
795  0 chooser.setFileView(new JalviewFileView());
796  0 chooser.setDialogTitle(
797    MessageManager.getString("label.save_tree_as_newick"));
798  0 chooser.setToolTipText(MessageManager.getString("action.save"));
799   
800  0 int value = chooser.showSaveDialog(null);
801   
802  0 if (value == JalviewFileChooser.APPROVE_OPTION)
803    {
804  0 String choice = chooser.getSelectedFile().getPath();
805  0 Cache.setProperty("LAST_DIRECTORY",
806    chooser.getSelectedFile().getParent());
807   
808  0 try
809    {
810  0 jalview.io.NewickFile fout = new jalview.io.NewickFile(
811    tree.getTopNode());
812  0 String output = fout.print(tree.hasBootstrap(), tree.hasDistances(),
813    tree.hasRootDistance());
814  0 java.io.PrintWriter out = new java.io.PrintWriter(
815    new java.io.FileWriter(choice));
816  0 out.println(output);
817  0 out.close();
818    } catch (Exception ex)
819    {
820  0 ex.printStackTrace();
821    }
822    }
823    }
824   
825    /**
826    * DOCUMENT ME!
827    *
828    * @param e
829    * DOCUMENT ME!
830    */
 
831  0 toggle @Override
832    public void printMenu_actionPerformed(ActionEvent e)
833    {
834    // Putting in a thread avoids Swing painting problems
835  0 treeCanvas.startPrinting();
836    }
837   
 
838  0 toggle @Override
839    public void originalSeqData_actionPerformed(ActionEvent e)
840    {
841  0 AlignmentView originalData = tree.getOriginalData();
842  0 if (originalData == null)
843    {
844  0 Console.info(
845    "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
846  0 return;
847    }
848    // decide if av alignment is sufficiently different to original data to
849    // warrant a new window to be created
850    // create new alignmnt window with hidden regions (unhiding hidden regions
851    // yields unaligned seqs)
852    // or create a selection box around columns in alignment view
853    // test Alignment(SeqCigar[])
854  0 char gc = '-';
855  0 try
856    {
857    // we try to get the associated view's gap character
858    // but this may fail if the view was closed...
859  0 gc = av.getGapCharacter();
860   
861    } catch (Exception ex)
862    {
863    }
864   
865  0 Object[] alAndColsel = originalData.getAlignmentAndHiddenColumns(gc);
866   
867  0 if (alAndColsel != null && alAndColsel[0] != null)
868    {
869    // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
870   
871  0 AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
872  0 AlignmentI dataset = (av != null && av.getAlignment() != null)
873    ? av.getAlignment().getDataset()
874    : null;
875  0 if (dataset != null)
876    {
877  0 al.setDataset(dataset);
878    }
879   
880  0 if (true)
881    {
882    // make a new frame!
883  0 AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1],
884    AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
885   
886    // >>>This is a fix for the moment, until a better solution is
887    // found!!<<<
888    // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
889   
890    // af.addSortByOrderMenuItem(ServiceName + " Ordering",
891    // msaorder);
892   
893  0 Desktop.addInternalFrame(af, MessageManager.formatMessage(
894    "label.original_data_for_params", new Object[]
895    { this.title }), AlignFrame.DEFAULT_WIDTH,
896    AlignFrame.DEFAULT_HEIGHT);
897    }
898    }
899    }
900   
901    /**
902    * DOCUMENT ME!
903    *
904    * @param e
905    * DOCUMENT ME!
906    */
 
907  3 toggle @Override
908    public void fitToWindow_actionPerformed(ActionEvent e)
909    {
910  3 treeCanvas.fitToWindow = fitToWindow.isSelected();
911  3 repaint();
912    }
913   
914    /** This method restores the menu options and view settings
915    * in the tree panel if the tree is based on annotation.
916    *
917    * @param viewName The type of annotation view in the loaded project
918    * : None, Labels, Coloured Lines.
919    */
 
920  4 toggle public void setShowAnnotationAs(String viewName)
921    {
922   
923  4 if (isAnnotationBased())
924    {
925    //setting showSecondaryStructureProviderMenu to true if the
926    //similarity is based on secondary structure
927  4 addSSProviderPanel();
928  4 showSecondaryStructureProviderMenu.setVisible(true);
929  4 setShowAnnotationAsView(viewName);
930  4 sortAnnotationAssocViews.setVisible(true);
931   
932    }
933  4 this.treeCanvas.revalidate();
934  4 revalidate();
935  4 repaint();
936    }
937   
938    /** This method restore the annotation view based on the view name
939    *
940    * @param viewName Name of the view name (for None, Labels, Coloured Lines)
941    * which matches with the text displayed in the button.
942    */
 
943  4 toggle public void setShowAnnotationAsView(String viewName)
944    {
945  4 for (AbstractButton button : Arrays.asList(showStructureProviderLabels,
946    showStructureProviderColouredLines, hideStructureProviders)) {
947  8 if (button.getText().equals(viewName)) {
948  4 button.doClick(); // Triggers the action event
949  4 return;
950    }
951    }
952    // The default view - None
953  0 hideStructureProviders.doClick();
954    }
955   
956    /**
957    * This method retrieves the text in the selected view button (for None,
958    * Labels, Coloured Lines) in the tree panel and returns it.
959    * @return The text in the selected view button
960    */
 
961  4 toggle public String getShowAnnotationAs() {
962  4 for (AbstractButton button : Arrays.asList(showStructureProviderLabels, showStructureProviderColouredLines, hideStructureProviders)) {
963  8 if (button.isSelected()) {
964  4 return button.getText(); // Gets the text
965    }
966    }
967  0 return hideStructureProviders.getText();
968    }
969   
970   
971    /**
972    * sort the associated alignment view by the current tree.
973    *
974    * @param e
975    */
 
976  1 toggle @Override
977    public void sortByTree_actionPerformed()
978    {
979   
980  1 if (treeCanvas.applyToAllViews)
981    {
982  0 final ArrayList<CommandI> commands = new ArrayList<>();
983  0 for (AlignmentPanel ap : PaintRefresher
984    .getAssociatedPanels(av.getSequenceSetId()))
985    {
986  0 commands.add(sortAlignmentIn(ap.av.getAlignPanel()));
987    }
988  0 av.getAlignPanel().alignFrame.addHistoryItem(new CommandI()
989    {
990   
 
991  0 toggle @Override
992    public void undoCommand(AlignmentI[] views)
993    {
994  0 for (CommandI tsort : commands)
995    {
996  0 tsort.undoCommand(views);
997    }
998    }
999   
 
1000  0 toggle @Override
1001    public int getSize()
1002    {
1003  0 return commands.size();
1004    }
1005   
 
1006  0 toggle @Override
1007    public String getDescription()
1008    {
1009  0 return "Tree Sort (many views)";
1010    }
1011   
 
1012  0 toggle @Override
1013    public void doCommand(AlignmentI[] views)
1014    {
1015   
1016  0 for (CommandI tsort : commands)
1017    {
1018  0 tsort.doCommand(views);
1019    }
1020    }
1021    });
1022  0 for (AlignmentPanel ap : PaintRefresher
1023    .getAssociatedPanels(av.getSequenceSetId()))
1024    {
1025    // ensure all the alignFrames refresh their GI after adding an undo item
1026  0 ap.alignFrame.updateEditMenuBar();
1027    }
1028    }
1029    else
1030    {
1031  1 treeCanvas.getAssociatedPanel().alignFrame.addHistoryItem(
1032    sortAlignmentIn(treeCanvas.getAssociatedPanel()));
1033    }
1034   
1035    }
1036   
1037    /**
1038    * This method sort the annotation panel by tree. The sorted annotation rows
1039    * from tree appear first followed by other annotation rows in the original
1040    * order. Sort annotation by sequence/label overrides sort annotation by tree.
1041    */
 
1042  0 toggle @Override
1043    public void sortAnnotationByTree_actionPerformed()
1044    {
1045  0 if (getAlignment() != null
1046    && getAlignment().getAlignmentAnnotation() != null
1047    && getAlignment().getAlignmentAnnotation().length > 0)
1048    {
1049    // Initialize annotation sorter with the alignment
1050  0 final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),
1051    av.isShowAutocalculatedAbove());
1052   
1053    // Sort by tree
1054  0 sorter.sortByTree(getAlignment().getAlignmentAnnotation(), tree);
1055  0 av.setSortAnnotationsBy(SequenceAnnotationOrder.NONE);
1056    // Repaint the alignment panel after the sort
1057  0 av.getAlignPanel().repaint();
1058    }
1059    }
1060   
1061   
 
1062  1 toggle public CommandI sortAlignmentIn(AlignmentPanel ap)
1063    {
1064    // TODO: move to alignment view controller
1065  1 AlignmentViewport viewport = ap.av;
1066  1 SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray();
1067  1 AlignmentSorter.sortByTree(viewport.getAlignment(), tree);
1068  1 CommandI undo;
1069  1 undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment());
1070   
1071  1 ap.paintAlignment(true, false);
1072  1 return undo;
1073    }
1074   
1075    /**
1076    * DOCUMENT ME!
1077    *
1078    * @param e
1079    * DOCUMENT ME!
1080    */
 
1081  0 toggle @Override
1082    public void font_actionPerformed(ActionEvent e)
1083    {
1084  0 if (treeCanvas == null)
1085    {
1086  0 return;
1087    }
1088   
1089  0 new FontChooser(this);
1090    }
1091   
 
1092  46 toggle public Font getTreeFont()
1093    {
1094  46 return treeCanvas.font;
1095    }
1096   
 
1097  3 toggle public void setTreeFont(Font f)
1098    {
1099  3 if (treeCanvas != null)
1100    {
1101  3 treeCanvas.setFont(f);
1102    }
1103    }
1104   
1105    /**
1106    * DOCUMENT ME!
1107    *
1108    * @param e
1109    * DOCUMENT ME!
1110    */
 
1111  0 toggle @Override
1112    public void distanceMenu_actionPerformed(ActionEvent e)
1113    {
1114  0 treeCanvas.setShowDistances(distanceMenu.isSelected());
1115    }
1116   
 
1117  0 toggle @Override
1118    public void hideStructureProviders_actionPerformed(ActionEvent e)
1119    {
1120  0 treeCanvas.hideStructureProviders(hideStructureProviders.isSelected());
1121   
1122    // Show the panel based on user selection
1123  0 CardLayout ssProviderCardLayout = (CardLayout) (ssProviderPanel.getLayout());
1124  0 if (showStructureProviderColouredLines.isSelected()) {
1125  0 ssProviderCardLayout.show(ssProviderPanel, "colorMapScrollPanel");
1126  0 ssProviderPanel.setVisible(true);
1127    }
1128    else {
1129  0 ssProviderPanel.setVisible(false);
1130    }
1131  0 ssProviderPanel.repaint();
1132    }
1133   
 
1134  4 toggle @Override
1135    public void showStructureProviderColouredLines_actionPerformed(ActionEvent e)
1136    {
1137  4 treeCanvas.setShowStructureProviderColouredLines(showStructureProviderColouredLines.isSelected());
1138   
1139    // Show the panel based on user selection
1140  4 CardLayout ssProviderCardLayout = (CardLayout) (ssProviderPanel.getLayout());
1141   
1142  4 if (showStructureProviderColouredLines.isSelected()) {
1143  4 ssProviderCardLayout.show(ssProviderPanel, "colorMapScrollPanel");
1144  4 ssProviderPanel.setVisible(true);
1145    }
1146    else {
1147  0 ssProviderPanel.setVisible(false);
1148    }
1149  4 ssProviderPanel.repaint();
1150    }
1151   
 
1152  0 toggle @Override
1153    public void showStructureProviderLabels_actionPerformed(ActionEvent e)
1154    {
1155  0 treeCanvas.setShowStructureProviderLabels(showStructureProviderLabels.isSelected());
1156   
1157    // Show the panel based on user selection
1158  0 CardLayout ssProviderCardLayout = (CardLayout) (ssProviderPanel.getLayout());
1159  0 if (showStructureProviderColouredLines.isSelected()) {
1160  0 ssProviderCardLayout.show(ssProviderPanel, "colorMapScrollPanel");
1161  0 ssProviderPanel.setVisible(true);
1162    }
1163    else {
1164  0 ssProviderPanel.setVisible(false);
1165    }
1166  0 ssProviderPanel.repaint();
1167    }
1168   
1169   
 
1170  0 toggle public void toggleStructureProviderColouredLine(String provider, boolean action)
1171    {
1172  0 treeCanvas.toggleStructureProviderColouredLine(provider, action);
1173    }
1174   
1175    /**
1176    * DOCUMENT ME!
1177    *
1178    * @param e
1179    * DOCUMENT ME!
1180    */
 
1181  0 toggle @Override
1182    public void bootstrapMenu_actionPerformed(ActionEvent e)
1183    {
1184  0 treeCanvas.setShowBootstrap(bootstrapMenu.isSelected());
1185    }
1186   
1187    /**
1188    * DOCUMENT ME!
1189    *
1190    * @param e
1191    * DOCUMENT ME!
1192    */
 
1193  0 toggle @Override
1194    public void placeholdersMenu_actionPerformed(ActionEvent e)
1195    {
1196  0 treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected());
1197    }
1198   
1199    /**
1200    * Outputs the Tree in image format (currently EPS or PNG). The user is
1201    * prompted for the file to save to, and for EPS (unless a preference is
1202    * already set) for the choice of Text or Lineart for character rendering.
1203    */
 
1204  0 toggle @Override
1205    public void writeTreeImage(TYPE imageFormat)
1206    {
1207  0 int width = treeCanvas.getWidth();
1208  0 int height = treeCanvas.getHeight();
1209  0 ImageWriterI writer = new ImageWriterI()
1210    {
 
1211  0 toggle @Override
1212    public void exportImage(Graphics g) throws Exception
1213    {
1214  0 treeCanvas.draw(g, width, height);
1215    }
1216    };
1217  0 String tree = MessageManager.getString("label.tree");
1218  0 ImageExporter exporter = new ImageExporter(writer, null, imageFormat,
1219    tree);
1220  0 try
1221    {
1222  0 exporter.doExport(null, this, width, height,
1223    tree.toLowerCase(Locale.ROOT));
1224    } catch (ImageOutputException ioex)
1225    {
1226  0 Console.error(
1227    "Unexpected error whilst writing " + imageFormat.toString(),
1228    ioex);
1229    }
1230    }
1231   
1232    /**
1233    * change node labels to the annotation referred to by labelClass TODO:
1234    * promote to a datamodel modification that can be undone TODO: make argument
1235    * one case of a generic transformation function ie { undoStep = apply(Tree,
1236    * TransformFunction)};
1237    *
1238    * @param labelClass
1239    */
 
1240  0 toggle public void changeNames(final String labelClass)
1241    {
1242  0 tree.applyToNodes(new NodeTransformI()
1243    {
1244   
 
1245  0 toggle @Override
1246    public void transform(BinaryNode node)
1247    {
1248  0 if (node instanceof SequenceNode
1249    && !((SequenceNode) node).isPlaceholder()
1250    && !((SequenceNode) node).isDummy())
1251    {
1252  0 String newname = null;
1253  0 SequenceI sq = (SequenceI) ((BinaryNode) node).element();
1254  0 if (sq != null)
1255    {
1256    // search dbrefs, features and annotation
1257  0 List<DBRefEntry> refs = jalview.util.DBRefUtils
1258    .selectRefs(sq.getDBRefs(), new String[]
1259    { labelClass.toUpperCase(Locale.ROOT) });
1260  0 if (refs != null)
1261    {
1262  0 for (int i = 0, ni = refs.size(); i < ni; i++)
1263    {
1264  0 if (newname == null)
1265    {
1266  0 newname = new String(refs.get(i).getAccessionId());
1267    }
1268    else
1269    {
1270  0 newname += "; " + refs.get(i).getAccessionId();
1271    }
1272    }
1273    }
1274  0 if (newname == null)
1275    {
1276  0 List<SequenceFeature> features = sq.getFeatures()
1277    .getPositionalFeatures(labelClass);
1278  0 for (SequenceFeature feature : features)
1279    {
1280  0 if (newname == null)
1281    {
1282  0 newname = feature.getDescription();
1283    }
1284    else
1285    {
1286  0 newname = newname + "; " + feature.getDescription();
1287    }
1288    }
1289    }
1290    }
1291  0 if (newname != null)
1292    {
1293    // String oldname = ((SequenceNode) node).getName();
1294    // TODO : save oldname in the undo object for this modification.
1295  0 ((BinaryNode) node).setName(newname);
1296    }
1297    }
1298    }
1299    });
1300    }
1301   
1302    /**
1303    * Formats a localised title for the tree panel, like
1304    * <p>
1305    * Neighbour Joining Using BLOSUM62
1306    * <p>
1307    * For a tree loaded from file, just uses the file name
1308    *
1309    * @return
1310    */
 
1311  0 toggle public String getPanelTitle()
1312    {
1313  0 if (treeTitle != null)
1314    {
1315  0 return treeTitle;
1316    }
1317   
1318    /*
1319    * i18n description of Neighbour Joining or Average Distance method
1320    */
1321  0 String treecalcnm = MessageManager.getString(
1322    "label.tree_calc_" + treeType.toLowerCase(Locale.ROOT));
1323   
1324    /*
1325    * short score model name (long description can be too long)
1326    */
1327  0 String smn = scoreModelName;
1328   
1329    /*
1330    * put them together as <method> Using <model>
1331    */
1332  0 final String ttl = MessageManager.formatMessage("label.calc_title",
1333    treecalcnm, smn);
1334  0 return ttl;
1335    }
1336   
1337    /**
1338    * Builds an EPS image and writes it to the specified file.
1339    *
1340    * @param outFile
1341    * @param textOption
1342    * true for Text character rendering, false for Lineart
1343    */
 
1344  0 toggle protected void writeEpsFile(File outFile, boolean textOption)
1345    {
1346  0 try
1347    {
1348  0 int width = treeCanvas.getWidth();
1349  0 int height = treeCanvas.getHeight();
1350   
1351  0 FileOutputStream out = new FileOutputStream(outFile);
1352  0 EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width,
1353    height);
1354  0 pg.setAccurateTextMode(!textOption);
1355  0 treeCanvas.draw(pg, width, height);
1356   
1357  0 pg.flush();
1358  0 pg.close();
1359    } catch (Exception ex)
1360    {
1361  0 jalview.bin.Console.errPrintln("Error writing tree as EPS");
1362  0 ex.printStackTrace();
1363    }
1364    }
1365   
 
1366  0 toggle public AlignViewport getViewport()
1367    {
1368  0 return av;
1369    }
1370   
 
1371  0 toggle public void setViewport(AlignViewport av)
1372    {
1373  0 this.av = av;
1374    }
1375   
 
1376  47 toggle public TreeCanvas getTreeCanvas()
1377    {
1378  47 return treeCanvas;
1379    }
1380   
1381    /** This method maps the tree nodes with annotation rows and respective sequences.
1382    *
1383    * @param alignPanel Alignment panel object
1384    * @param nf Loaded newick file
1385    * @param annotationIds Map of annotation ids and respective annotation rows
1386    * @param treeTitle Title of the tree panel
1387    * @param input Alignment view object
1388    * @return Returns tree panel with the nodes mapped with annotation rows and sequences
1389    */
 
1390  3 toggle public static TreePanel newTreeForAnnotations(AlignmentPanel alignPanel,
1391    NewickFile nf, Map<String, AlignmentAnnotation> annotationIds,
1392    String treeTitle, AlignmentView input)
1393    {
1394    // Create TreePanel instance
1395  3 TreePanel tp = new TreePanel(alignPanel, nf, treeTitle, input, null,
1396    "Annotation Tree", annotationIds);
1397  3 return tp;
1398    }
1399   
1400   
1401    /**
1402    * Processes tree nodes by applying transformations based on annotations.
1403    * Maps the nodes with annotation rows and/or sequences
1404    * @param tree
1405    * @param annotationIds
1406    */
 
1407  3 toggle private static void processTreeNodes(TreeModel tree,
1408    Map<String, AlignmentAnnotation> annotationIds)
1409    {
1410    // Initialise the node attribute in the tree model
1411    // object with the leaf nodes
1412  3 BinaryNode topNode = tree.getTopNode();
1413  3 Vector<BinaryNode> leafNodes = tree.findLeaves(topNode);
1414  3 tree.setNode(leafNodes);
1415  3 List<SequenceI> seqs = new ArrayList<SequenceI>();
1416    //Apply transformation: map nodes with annotation rows and sequences
1417  3 tree.applyToNodes(node -> {
1418  64 Object element = node.element();
1419  64 String nodeName = node.getName(); //Annotation id in case of annotation based tree
1420   
1421    // Extract annotation ID
1422  64 String annotId = nodeName.replaceAll("[^a-z_A-Z0-9]", "");
1423   
1424    //Get the annotation object
1425  64 AlignmentAnnotation annot = annotationIds.get(annotId);
1426   
1427  64 if (annot != null)
1428    {
1429    // Map the sequence and annotation with the node
1430  36 SequenceI seq = annot.sequenceRef;
1431    // TODO clean up the binary tree object inheritance to avoid casting
1432  36 node.setElement(seq);
1433  36 ((SequenceNode)node).setPlaceholder(false);
1434  36 node.setName(seq.getName());
1435   
1436    // Get annotation provider name from the annotation description
1437  36 String ssAnnotProvider = AlignmentAnnotationUtils
1438    .extractSSSourceFromAnnotationDescription(annot);
1439  36 node.setLabel(ssAnnotProvider);
1440  36 node.setAlignmentAnnotation(annot);
1441   
1442    // Initialise AnnotationDetails if present for the annotation
1443  36 if (annot.hasAnnotationDetailsProperty())
1444    {
1445  17 node.setAnnotationDetails(annot.getAnnotationDetailsProperty());
1446    }
1447   
1448    } else {
1449  28 if (Console.isTraceEnabled())
1450    {
1451  0 Console.trace("Annotation based tree: ignoring unmapped id "+annotId);
1452    }
1453   
1454  28 if (element instanceof SequenceI)
1455    {
1456    //If annotation is absent, set the default label ?
1457    // why ?
1458  28 node.setLabel(Constants.STRUCTURE_PROVIDERS.get("None"));
1459    }
1460    }
1461    });
1462    }
1463    }