in progress
[jalview.git] / forester / java / src / org / forester / archaeopteryx / TreePanel.java
1 // $Id:
2 // FORESTER -- software libraries and applications
3 // for evolutionary biology research and applications.
4 //
5 // Copyright (C) 2008-2009 Christian M. Zmasek
6 // Copyright (C) 2008-2009 Burnham Institute for Medical Research
7 // All rights reserved
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 //
23 // Contact: phylosoft @ gmail . com
24 // WWW: www.phylosoft.org/forester
25
26 package org.forester.archaeopteryx;
27
28 import java.awt.BasicStroke;
29 import java.awt.Color;
30 import java.awt.Cursor;
31 import java.awt.Dimension;
32 import java.awt.Font;
33 import java.awt.GradientPaint;
34 import java.awt.Graphics;
35 import java.awt.Graphics2D;
36 import java.awt.Point;
37 import java.awt.Polygon;
38 import java.awt.Rectangle;
39 import java.awt.RenderingHints;
40 import java.awt.event.ActionEvent;
41 import java.awt.event.ActionListener;
42 import java.awt.event.FocusAdapter;
43 import java.awt.event.FocusEvent;
44 import java.awt.event.InputEvent;
45 import java.awt.event.KeyAdapter;
46 import java.awt.event.KeyEvent;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.MouseWheelEvent;
49 import java.awt.event.MouseWheelListener;
50 import java.awt.font.FontRenderContext;
51 import java.awt.font.TextLayout;
52 import java.awt.geom.AffineTransform;
53 import java.awt.geom.Arc2D;
54 import java.awt.geom.CubicCurve2D;
55 import java.awt.geom.Ellipse2D;
56 import java.awt.geom.Line2D;
57 import java.awt.geom.QuadCurve2D;
58 import java.awt.geom.Rectangle2D;
59 import java.awt.image.BufferedImage;
60 import java.awt.print.PageFormat;
61 import java.awt.print.Printable;
62 import java.awt.print.PrinterException;
63 import java.io.File;
64 import java.io.IOException;
65 import java.io.UnsupportedEncodingException;
66 import java.net.URI;
67 import java.net.URISyntaxException;
68 import java.net.URLEncoder;
69 import java.text.DecimalFormat;
70 import java.text.DecimalFormatSymbols;
71 import java.text.NumberFormat;
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.HashSet;
76 import java.util.Hashtable;
77 import java.util.List;
78 import java.util.Set;
79
80 import javax.swing.BorderFactory;
81 import javax.swing.JApplet;
82 import javax.swing.JColorChooser;
83 import javax.swing.JDialog;
84 import javax.swing.JMenuItem;
85 import javax.swing.JOptionPane;
86 import javax.swing.JPanel;
87 import javax.swing.JPopupMenu;
88 import javax.swing.JTextArea;
89 import javax.swing.Popup;
90 import javax.swing.PopupFactory;
91
92 import org.forester.archaeopteryx.ControlPanel.NodeClickAction;
93 import org.forester.archaeopteryx.Options.CLADOGRAM_TYPE;
94 import org.forester.archaeopteryx.Options.NODE_LABEL_DIRECTION;
95 import org.forester.archaeopteryx.Options.PHYLOGENY_GRAPHICS_TYPE;
96 import org.forester.archaeopteryx.phylogeny.data.RenderableDomainArchitecture;
97 import org.forester.archaeopteryx.phylogeny.data.RenderableVector;
98 import org.forester.archaeopteryx.tools.Blast;
99 import org.forester.archaeopteryx.tools.ImageLoader;
100 import org.forester.io.parsers.phyloxml.PhyloXmlUtil;
101 import org.forester.phylogeny.Phylogeny;
102 import org.forester.phylogeny.PhylogenyMethods;
103 import org.forester.phylogeny.PhylogenyMethods.DESCENDANT_SORT_PRIORITY;
104 import org.forester.phylogeny.PhylogenyNode;
105 import org.forester.phylogeny.data.Annotation;
106 import org.forester.phylogeny.data.BranchColor;
107 import org.forester.phylogeny.data.Confidence;
108 import org.forester.phylogeny.data.Event;
109 import org.forester.phylogeny.data.NodeVisualization;
110 import org.forester.phylogeny.data.NodeVisualization.NodeFill;
111 import org.forester.phylogeny.data.NodeVisualization.NodeShape;
112 import org.forester.phylogeny.data.PhylogenyData;
113 import org.forester.phylogeny.data.PhylogenyDataUtil;
114 import org.forester.phylogeny.data.PropertiesMap;
115 import org.forester.phylogeny.data.Property;
116 import org.forester.phylogeny.data.Sequence;
117 import org.forester.phylogeny.data.SequenceRelation;
118 import org.forester.phylogeny.data.Taxonomy;
119 import org.forester.phylogeny.data.Uri;
120 import org.forester.phylogeny.iterators.PhylogenyNodeIterator;
121 import org.forester.phylogeny.iterators.PreorderTreeIterator;
122 import org.forester.util.BasicDescriptiveStatistics;
123 import org.forester.util.DescriptiveStatistics;
124 import org.forester.util.ForesterConstants;
125 import org.forester.util.ForesterUtil;
126
127 public final class TreePanel extends JPanel implements ActionListener, MouseWheelListener, Printable {
128
129     private static final float              PI                                = ( float ) ( Math.PI );
130     private static final double             TWO_PI                            = 2 * Math.PI;
131     private static final float              ONEHALF_PI                        = ( float ) ( 1.5 * Math.PI );
132     private static final float              HALF_PI                           = ( float ) ( Math.PI / 2.0 );
133     private static final float              ANGLE_ROTATION_UNIT               = ( float ) ( Math.PI / 32 );
134     private static final short              OV_BORDER                         = 10;
135     final static Cursor                     CUT_CURSOR                        = Cursor.getPredefinedCursor( Cursor.CROSSHAIR_CURSOR );
136     final static Cursor                     MOVE_CURSOR                       = Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR );
137     final static Cursor                     ARROW_CURSOR                      = Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR );
138     final static Cursor                     HAND_CURSOR                       = Cursor.getPredefinedCursor( Cursor.HAND_CURSOR );
139     final static Cursor                     WAIT_CURSOR                       = Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR );
140     private final static long               serialVersionUID                  = -978349745916505029L;
141     private final static int                EURO_D                            = 10;
142     private final static String             NODE_POPMENU_NODE_CLIENT_PROPERTY = "node";
143     private final static int                MIN_ROOT_LENGTH                   = 3;
144     private final static int                MAX_SUBTREES                      = 100;
145     private final static int                MAX_NODE_FRAMES                   = 10;
146     private final static int                MOVE                              = 20;
147     private final static NumberFormat       FORMATTER_CONFIDENCE;
148     private final static NumberFormat       FORMATTER_BRANCH_LENGTH;
149     private final static int                WIGGLE                            = 2;
150     private final static int                LIMIT_FOR_HQ_RENDERING            = 1000;
151     private final static int                CONFIDENCE_LEFT_MARGIN            = 4;
152     // TODO "rendering_hints" was static before. Need to make sure everything is OK with it not
153     // being static anymore (02/20/2009).
154     private final RenderingHints            _rendering_hints                  = new RenderingHints( RenderingHints.KEY_RENDERING,
155                                                                                                     RenderingHints.VALUE_RENDER_DEFAULT );
156     private File                            _treefile                         = null;
157     private Configuration                   _configuration                    = null;
158     private final NodeFrame[]               _node_frames                      = new NodeFrame[ TreePanel.MAX_NODE_FRAMES ];
159     private int                             _node_frame_index                 = 0;
160     private Phylogeny                       _phylogeny                        = null;
161     private final Phylogeny[]               _sub_phylogenies                  = new Phylogeny[ TreePanel.MAX_SUBTREES ];
162     private final PhylogenyNode[]           _sub_phylogenies_temp_roots       = new PhylogenyNode[ TreePanel.MAX_SUBTREES ];
163     private int                             _subtree_index                    = 0;
164     private MainPanel                       _main_panel                       = null;
165     private Set<Integer>                    _found_nodes                      = null;
166     private PhylogenyNode                   _highlight_node                   = null;
167     private JPopupMenu                      _node_popup_menu                  = null;
168     private JMenuItem                       _node_popup_menu_items[]          = null;
169     private int                             _longest_ext_node_info            = 0;
170     private float                           _x_correction_factor              = 0.0f;
171     private float                           _ov_x_correction_factor           = 0.0f;
172     private float                           _x_distance                       = 0.0f;
173     private float                           _y_distance                       = 0.0f;
174     private PHYLOGENY_GRAPHICS_TYPE         _graphics_type                    = PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR;
175     private double                          _domain_structure_width           = Constants.DOMAIN_STRUCTURE_DEFAULT_WIDTH;
176     private int                             _domain_structure_e_value_thr_exp = Constants.DOMAIN_STRUCTURE_E_VALUE_THR_DEFAULT_EXP;
177     private float                           _last_drag_point_x                = 0;
178     private float                           _last_drag_point_y                = 0;
179     private ControlPanel                    _control_panel                    = null;
180     private int                             _external_node_index              = 0;
181     private final Polygon                   _polygon                          = new Polygon();
182     private final StringBuilder             _sb                               = new StringBuilder();
183     private JColorChooser                   _color_chooser                    = null;
184     private double                          _scale_distance                   = 0.0;
185     private String                          _scale_label                      = null;
186     private final CubicCurve2D              _cubic_curve                      = new CubicCurve2D.Float();
187     private final QuadCurve2D               _quad_curve                       = new QuadCurve2D.Float();
188     private final Line2D                    _line                             = new Line2D.Float();
189     private final Ellipse2D                 _ellipse                          = new Ellipse2D.Float();
190     private final Rectangle2D               _rectangle                        = new Rectangle2D.Float();
191     private Options                         _options                          = null;
192     private float                           _ov_max_width                     = 0;
193     private float                           _ov_max_height                    = 0;
194     private int                             _ov_x_position                    = 0;
195     private int                             _ov_y_position                    = 0;
196     private int                             _ov_y_start                       = 0;
197     private float                           _ov_y_distance                    = 0;
198     private float                           _ov_x_distance                    = 0;
199     private boolean                         _ov_on                            = false;
200     private double                          _urt_starting_angle               = ( float ) ( Math.PI / 2 );
201     private float                           _urt_factor                       = 1;
202     private float                           _urt_factor_ov                    = 1;
203     private final boolean                   _phy_has_branch_lengths;
204     private final Rectangle2D               _ov_rectangle                     = new Rectangle2D.Float();
205     private boolean                         _in_ov_rect                       = false;
206     private boolean                         _in_ov                            = false;
207     private final Rectangle                 _ov_virtual_rectangle             = new Rectangle();
208     final private static double             _180_OVER_PI                      = 180.0 / Math.PI;
209     private static final float              ROUNDED_D                         = 8;
210     private int                             _circ_max_depth;
211     private PhylogenyNode                   _root;
212     final private Arc2D                     _arc                              = new Arc2D.Double();
213     final private HashMap<Integer, Double>  _urt_nodeid_angle_map             = new HashMap<Integer, Double>();
214     final private HashMap<Integer, Integer> _urt_nodeid_index_map             = new HashMap<Integer, Integer>();
215     final private Set<Integer>              _collapsed_external_nodeid_set    = new HashSet<Integer>();
216     HashMap<Integer, Short>                 _nodeid_dist_to_leaf              = new HashMap<Integer, Short>();
217     private AffineTransform                 _at;
218     private double                          _max_distance_to_root             = -1;
219     private int                             _dynamic_hiding_factor            = 0;
220     private boolean                         _edited                           = false;
221     private Popup                           _node_desc_popup;
222     private JTextArea                       _rollover_popup;
223     // private final int                       _box_size;
224     // private final int                       _half_box_size;
225     //private final short                     _skip_counter                     = 0;
226     private final StringBuffer              _popup_buffer                     = new StringBuffer();
227     final private static Font               POPUP_FONT                        = new Font( Configuration.getDefaultFontFamilyName(),
228                                                                                           Font.PLAIN,
229                                                                                           12 );
230     private static final boolean            DRAW_MEAN_COUNTS                  = true;                                                     //TODO remove me later
231     private Sequence                        _query_sequence                   = null;
232     private final FontRenderContext         _frc                              = new FontRenderContext( null,
233                                                                                                        false,
234                                                                                                        false );
235     // expression values menu:
236     private DescriptiveStatistics           _statistics_for_vector_data;
237     private PhylogenyNode[]                 _nodes_in_preorder                = null;
238     //  private Image                           offscreenImage;
239     //  private Graphics                        offscreenGraphics;
240     //  private Dimension                       offscreenDimension;
241     static {
242         final DecimalFormatSymbols dfs = new DecimalFormatSymbols();
243         dfs.setDecimalSeparator( '.' );
244         FORMATTER_CONFIDENCE = new DecimalFormat( "#.###", dfs );
245         FORMATTER_BRANCH_LENGTH = new DecimalFormat( "#.###", dfs );
246     }
247
248     TreePanel( final Phylogeny t, final Configuration configuration, final MainPanel tjp ) {
249         requestFocusInWindow();
250         addKeyListener( new KeyAdapter() {
251
252             @Override
253             public void keyPressed( final KeyEvent key_event ) {
254                 keyPressedCalls( key_event );
255                 requestFocusInWindow();
256             }
257         } );
258         addFocusListener( new FocusAdapter() {
259
260             @Override
261             public void focusGained( final FocusEvent e ) {
262                 requestFocusInWindow();
263             }
264         } );
265         if ( ( t == null ) || t.isEmpty() ) {
266             throw new IllegalArgumentException( "attempt to draw phylogeny which is null or empty" );
267         }
268         _graphics_type = tjp.getOptions().getPhylogenyGraphicsType();
269         _main_panel = tjp;
270         _configuration = configuration;
271         _phylogeny = t;
272         _phy_has_branch_lengths = AptxUtil.isHasAtLeastOneBranchLengthLargerThanZero( _phylogeny );
273         init();
274         // if ( !_phylogeny.isEmpty() ) {
275         _phylogeny.recalculateNumberOfExternalDescendants( true );
276         checkForVectorProperties( _phylogeny );
277         // }
278         setBackground( getTreeColorSet().getBackgroundColor() );
279         final MouseListener mouse_listener = new MouseListener( this );
280         addMouseListener( mouse_listener );
281         addMouseMotionListener( mouse_listener );
282         addMouseWheelListener( this );
283         calculateScaleDistance();
284         FORMATTER_CONFIDENCE.setMaximumFractionDigits( configuration.getNumberOfDigitsAfterCommaForConfidenceValues() );
285         FORMATTER_BRANCH_LENGTH.setMaximumFractionDigits( configuration
286                 .getNumberOfDigitsAfterCommaForBranchLengthValues() );
287     }
288
289     public void checkForVectorProperties( final Phylogeny phy ) {
290         final DescriptiveStatistics stats = new BasicDescriptiveStatistics();
291         for( final PhylogenyNodeIterator iter = phy.iteratorPreorder(); iter.hasNext(); ) {
292             final PhylogenyNode node = iter.next();
293             if ( node.getNodeData().getProperties() != null ) {
294                 final PropertiesMap pm = node.getNodeData().getProperties();
295                 final double[] vector = new double[ pm.getProperties().size() ];
296                 int counter = 0;
297                 for( final String ref : pm.getProperties().keySet() ) {
298                     if ( ref.startsWith( PhyloXmlUtil.VECTOR_PROPERTY_REF ) ) {
299                         final Property p = pm.getProperty( ref );
300                         final String value_str = p.getValue();
301                         final String index_str = ref
302                                 .substring( PhyloXmlUtil.VECTOR_PROPERTY_REF.length(), ref.length() );
303                         double d = -100;
304                         try {
305                             d = Double.parseDouble( value_str );
306                         }
307                         catch ( final NumberFormatException e ) {
308                             JOptionPane.showMessageDialog( this, "Could not parse \"" + value_str
309                                     + "\" into a decimal value", "Problem with Vector Data", JOptionPane.ERROR_MESSAGE );
310                             return;
311                         }
312                         int i = -1;
313                         try {
314                             i = Integer.parseInt( index_str );
315                         }
316                         catch ( final NumberFormatException e ) {
317                             JOptionPane.showMessageDialog( this,
318                                                            "Could not parse \"" + index_str
319                                                                    + "\" into index for vector data",
320                                                            "Problem with Vector Data",
321                                                            JOptionPane.ERROR_MESSAGE );
322                             return;
323                         }
324                         if ( i < 0 ) {
325                             JOptionPane.showMessageDialog( this,
326                                                            "Attempt to use negative index for vector data",
327                                                            "Problem with Vector Data",
328                                                            JOptionPane.ERROR_MESSAGE );
329                             return;
330                         }
331                         vector[ i ] = d;
332                         ++counter;
333                         stats.addValue( d );
334                     }
335                 }
336                 final List<Double> vector_l = new ArrayList<Double>( counter );
337                 for( int i = 0; i < counter; ++i ) {
338                     vector_l.add( vector[ i ] );
339                 }
340                 node.getNodeData().setVector( vector_l );
341             }
342         }
343         if ( stats.getN() > 0 ) {
344             _statistics_for_vector_data = stats;
345         }
346     }
347
348     final public void actionPerformed( final ActionEvent e ) {
349         boolean done = false;
350         final JMenuItem node_popup_menu_item = ( JMenuItem ) e.getSource();
351         for( int index = 0; ( index < _node_popup_menu_items.length ) && !done; index++ ) {
352             // NOTE: index corresponds to the indices of click-to options
353             // in the control panel.
354             if ( node_popup_menu_item == _node_popup_menu_items[ index ] ) {
355                 // Set this as the new default click-to action
356                 _main_panel.getControlPanel().setClickToAction( index );
357                 final PhylogenyNode node = ( PhylogenyNode ) _node_popup_menu
358                         .getClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY );
359                 handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
360                 done = true;
361             }
362         }
363         repaint();
364         requestFocusInWindow();
365     }
366
367     final private void addEmptyNode( final PhylogenyNode node ) {
368         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
369             errorMessageNoCutCopyPasteInUnrootedDisplay();
370             return;
371         }
372         final String label = getASimpleTextRepresentationOfANode( node );
373         String msg = "";
374         if ( ForesterUtil.isEmpty( label ) ) {
375             msg = "How to add the new, empty node?";
376         }
377         else {
378             msg = "How to add the new, empty node to node" + label + "?";
379         }
380         final Object[] options = { "As sibling", "As descendant", "Cancel" };
381         final int r = JOptionPane.showOptionDialog( this,
382                                                     msg,
383                                                     "Addition of Empty New Node",
384                                                     JOptionPane.CLOSED_OPTION,
385                                                     JOptionPane.QUESTION_MESSAGE,
386                                                     null,
387                                                     options,
388                                                     options[ 2 ] );
389         boolean add_as_sibling = true;
390         if ( r == 1 ) {
391             add_as_sibling = false;
392         }
393         else if ( r != 0 ) {
394             return;
395         }
396         final Phylogeny phy = new Phylogeny();
397         phy.setRoot( new PhylogenyNode() );
398         phy.setRooted( true );
399         if ( add_as_sibling ) {
400             if ( node.isRoot() ) {
401                 JOptionPane.showMessageDialog( this,
402                                                "Cannot add sibling to root",
403                                                "Attempt to add sibling to root",
404                                                JOptionPane.ERROR_MESSAGE );
405                 return;
406             }
407             phy.addAsSibling( node );
408         }
409         else {
410             phy.addAsChild( node );
411         }
412         setNodeInPreorderToNull();
413         _phylogeny.externalNodesHaveChanged();
414         _phylogeny.clearHashIdToNodeMap();
415         _phylogeny.recalculateNumberOfExternalDescendants( true );
416         resetNodeIdToDistToLeafMap();
417         setEdited( true );
418         repaint();
419     }
420
421     final private void assignGraphicsForBranchWithColorForParentBranch( final PhylogenyNode node,
422                                                                         final boolean is_vertical,
423                                                                         final Graphics g,
424                                                                         final boolean to_pdf,
425                                                                         final boolean to_graphics_file ) {
426         final NodeClickAction action = _control_panel.getActionWhenNodeClicked();
427         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
428             g.setColor( Color.BLACK );
429         }
430         else if ( ( ( action == NodeClickAction.COPY_SUBTREE ) || ( action == NodeClickAction.CUT_SUBTREE )
431                 || ( action == NodeClickAction.DELETE_NODE_OR_SUBTREE ) || ( action == NodeClickAction.PASTE_SUBTREE ) || ( action == NodeClickAction.ADD_NEW_NODE ) )
432                 && ( getCutOrCopiedTree() != null )
433                 && ( getCopiedAndPastedNodes() != null )
434                 && !to_pdf
435                 && !to_graphics_file && getCopiedAndPastedNodes().contains( node.getId() ) ) {
436             g.setColor( getTreeColorSet().getFoundColor() );
437         }
438         else if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
439             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
440         }
441         else if ( to_pdf ) {
442             g.setColor( getTreeColorSet().getBranchColorForPdf() );
443         }
444         else {
445             g.setColor( getTreeColorSet().getBranchColor() );
446         }
447     }
448
449     final Color getGraphicsForNodeBoxWithColorForParentBranch( final PhylogenyNode node ) {
450         if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
451             return ( PhylogenyMethods.getBranchColorValue( node ) );
452         }
453         else {
454             return ( getTreeColorSet().getBranchColor() );
455         }
456     }
457
458     final private void blast( final PhylogenyNode node ) {
459         if ( !isCanBlast( node ) ) {
460             JOptionPane.showMessageDialog( this,
461                                            "No sequence information present",
462                                            "Cannot Blast",
463                                            JOptionPane.WARNING_MESSAGE );
464             return;
465         }
466         if ( node.getNodeData().isHasSequence() ) {
467             final String query = Blast.obtainQueryForBlast( node );
468             boolean nucleotide = false;
469             if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getType() ) ) {
470                 if ( !node.getNodeData().getSequence().getType().toLowerCase().equals( PhyloXmlUtil.SEQ_TYPE_PROTEIN ) ) {
471                     nucleotide = true;
472                 }
473             }
474             else if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) {
475                 nucleotide = !ForesterUtil.seqIsLikelyToBeAa( node.getNodeData().getSequence().getMolecularSequence() );
476             }
477             if ( !ForesterUtil.isEmpty( query ) ) {
478                 JApplet applet = null;
479                 if ( isApplet() ) {
480                     applet = obtainApplet();
481                 }
482                 try {
483                     Blast.openNcbiBlastWeb( query, nucleotide, applet, this );
484                 }
485                 catch ( final Exception e ) {
486                     e.printStackTrace();
487                 }
488                 if ( Constants.ALLOW_DDBJ_BLAST ) {
489                     try {
490                         System.out.println( "trying: " + query );
491                         final Blast s = new Blast();
492                         s.ddbjBlast( query );
493                     }
494                     catch ( final Exception e ) {
495                         e.printStackTrace();
496                     }
497                 }
498             }
499         }
500     }
501
502     final void calcMaxDepth() {
503         if ( _phylogeny != null ) {
504             _circ_max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
505         }
506     }
507
508     /**
509      * Calculate the length of the distance between the given node and its
510      * parent.
511      * 
512      * @param node
513      * @param ext_node_x
514      * @factor
515      * @return the distance value
516      */
517     final private float calculateBranchLengthToParent( final PhylogenyNode node, final float factor ) {
518         if ( getControlPanel().isDrawPhylogram() ) {
519             if ( node.getDistanceToParent() < 0.0 ) {
520                 return 0.0f;
521             }
522             return ( float ) ( getXcorrectionFactor() * node.getDistanceToParent() );
523         }
524         else {
525             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
526                 return getXdistance();
527             }
528             return getXdistance() * factor;
529         }
530     }
531
532     final private Color calculateColorForAnnotation( final PhylogenyData ann ) {
533         Color c = getTreeColorSet().getAnnotationColor();
534         if ( getControlPanel().isColorAccordingToAnnotation() && ( getControlPanel().getAnnotationColors() != null ) ) {
535             c = getControlPanel().getAnnotationColors().get( ann.asSimpleText().toString() );
536             if ( c == null ) {
537                 c = getTreeColorSet().getAnnotationColor();
538             }
539         }
540         return c;
541     }
542
543     final void calculateLongestExtNodeInfo() {
544         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
545             return;
546         }
547         int max_length = ForesterUtil.roundToInt( ( getSize().getWidth() - MOVE )
548                 * Constants.EXT_NODE_INFO_LENGTH_MAX_RATIO );
549         if ( max_length < 40 ) {
550             max_length = 40;
551         }
552         int longest = 30;
553         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
554             int sum = 0;
555             if ( node.isCollapse() ) {
556                 continue;
557             }
558             if ( getControlPanel().isShowNodeNames() ) {
559                 sum += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
560             }
561             if ( node.getNodeData().isHasSequence() ) {
562                 if ( getControlPanel().isShowSequenceAcc()
563                         && ( node.getNodeData().getSequence().getAccession() != null ) ) {
564                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAccession()
565                             .getValue()
566                             + " " );
567                 }
568                 if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
569                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName() + " " );
570                 }
571                 if ( getControlPanel().isShowGeneSymbols()
572                         && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
573                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getSymbol() + " " );
574                 }
575                 if ( getControlPanel().isShowAnnotation()
576                         && ( node.getNodeData().getSequence().getAnnotations() != null )
577                         && !node.getNodeData().getSequence().getAnnotations().isEmpty() ) {
578                     sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAnnotation( 0 )
579                             .asSimpleText()
580                             + " " );
581                 }
582                 if ( getControlPanel().isShowDomainArchitectures()
583                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
584                     sum += ( ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture() )
585                             .getRenderingSize().getWidth();
586                 }
587             }
588             if ( node.getNodeData().isHasTaxonomy() ) {
589                 final Taxonomy tax = node.getNodeData().getTaxonomy();
590                 if ( getControlPanel().isShowTaxonomyCode() && !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
591                     sum += getTreeFontSet()._fm_large_italic.stringWidth( tax.getTaxonomyCode() + " " );
592                 }
593                 if ( getControlPanel().isShowTaxonomyScientificNames()
594                         && !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
595                     sum += getTreeFontSet()._fm_large_italic.stringWidth( tax.getScientificName() + " " );
596                 }
597                 if ( getControlPanel().isShowTaxonomyCommonNames() && !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
598                     sum += getTreeFontSet()._fm_large_italic.stringWidth( tax.getCommonName() + " ()" );
599                 }
600             }
601             if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
602                 sum += getTreeFontSet()._fm_large.stringWidth( propertiesToString( node ).toString() );
603             }
604             if ( getControlPanel().isShowBinaryCharacters() && node.getNodeData().isHasBinaryCharacters() ) {
605                 sum += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getBinaryCharacters()
606                         .getGainedCharactersAsStringBuffer().toString() );
607             }
608             if ( sum >= max_length ) {
609                 setLongestExtNodeInfo( max_length );
610                 return;
611             }
612             if ( sum > longest ) {
613                 longest = sum;
614             }
615         }
616         if ( longest >= max_length ) {
617             setLongestExtNodeInfo( max_length );
618         }
619         else {
620             setLongestExtNodeInfo( longest );
621         }
622     }
623
624     final private float calculateOvBranchLengthToParent( final PhylogenyNode node, final int factor ) {
625         if ( getControlPanel().isDrawPhylogram() ) {
626             if ( node.getDistanceToParent() < 0.0 ) {
627                 return 0.0f;
628             }
629             return ( float ) ( getOvXcorrectionFactor() * node.getDistanceToParent() );
630         }
631         else {
632             if ( ( factor == 0 ) || isNonLinedUpCladogram() ) {
633                 return getOvXDistance();
634             }
635             return getOvXDistance() * factor;
636         }
637     }
638
639     final void calculateScaleDistance() {
640         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
641             return;
642         }
643         final double height = getMaxDistanceToRoot();
644         if ( height > 0 ) {
645             if ( ( height <= 0.5 ) ) {
646                 setScaleDistance( 0.01 );
647             }
648             else if ( height <= 5.0 ) {
649                 setScaleDistance( 0.1 );
650             }
651             else if ( height <= 50.0 ) {
652                 setScaleDistance( 1 );
653             }
654             else if ( height <= 500.0 ) {
655                 setScaleDistance( 10 );
656             }
657             else {
658                 setScaleDistance( 100 );
659             }
660         }
661         else {
662             setScaleDistance( 0.0 );
663         }
664         String scale_label = String.valueOf( getScaleDistance() );
665         if ( !ForesterUtil.isEmpty( _phylogeny.getDistanceUnit() ) ) {
666             scale_label += " [" + _phylogeny.getDistanceUnit() + "]";
667         }
668         setScaleLabel( scale_label );
669     }
670
671     final Color calculateTaxonomyBasedColor( final Taxonomy tax ) {
672         String species = tax.getTaxonomyCode();
673         if ( ForesterUtil.isEmpty( species ) ) {
674             species = tax.getScientificName();
675             if ( ForesterUtil.isEmpty( species ) ) {
676                 species = tax.getCommonName();
677             }
678         }
679         if ( ForesterUtil.isEmpty( species ) ) {
680             return getTreeColorSet().getTaxonomyColor();
681         }
682         // Look in species hash
683         Color c = getControlPanel().getSpeciesColors().get( species );
684         if ( c == null ) {
685             c = AptxUtil.calculateColorFromString( species );
686             getControlPanel().getSpeciesColors().put( species, c );
687         }
688         return c;
689     }
690
691     final private void cannotOpenBrowserWarningMessage( final String type_type ) {
692         JOptionPane.showMessageDialog( this,
693                                        "Cannot launch web browser for " + type_type + " data of this node",
694                                        "Cannot launch web browser",
695                                        JOptionPane.WARNING_MESSAGE );
696     }
697
698     /**
699      * Collapse the tree from the given node
700      * 
701      * @param node
702      *            a PhylogenyNode
703      */
704     final void collapse( final PhylogenyNode node ) {
705         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
706             JOptionPane.showMessageDialog( this,
707                                            "Cannot collapse in unrooted display type",
708                                            "Attempt to collapse in unrooted display",
709                                            JOptionPane.WARNING_MESSAGE );
710             return;
711         }
712         if ( !node.isExternal() && !node.isRoot() ) {
713             final boolean collapse = !node.isCollapse();
714             AptxUtil.collapseSubtree( node, collapse );
715             updateSetOfCollapsedExternalNodes();
716             _phylogeny.recalculateNumberOfExternalDescendants( true );
717             resetNodeIdToDistToLeafMap();
718             calculateLongestExtNodeInfo();
719             setNodeInPreorderToNull();
720             _control_panel.displayedPhylogenyMightHaveChanged( true );
721             resetPreferredSize();
722             updateOvSizes();
723             _main_panel.adjustJScrollPane();
724             repaint();
725         }
726     }
727
728     final void collapseSpeciesSpecificSubtrees() {
729         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
730             return;
731         }
732         setWaitCursor();
733         AptxUtil.collapseSpeciesSpecificSubtrees( _phylogeny );
734         updateSetOfCollapsedExternalNodes();
735         _phylogeny.recalculateNumberOfExternalDescendants( true );
736         resetNodeIdToDistToLeafMap();
737         calculateLongestExtNodeInfo();
738         setNodeInPreorderToNull();
739         resetPreferredSize();
740         _main_panel.adjustJScrollPane();
741         setArrowCursor();
742         repaint();
743     }
744
745     final private void colorizeSubtree( final Color c, final PhylogenyNode node ) {
746         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
747             JOptionPane.showMessageDialog( this,
748                                            "Cannot colorize subtree in unrooted display type",
749                                            "Attempt to colorize subtree in unrooted display",
750                                            JOptionPane.WARNING_MESSAGE );
751             return;
752         }
753         _control_panel.setColorBranches( true );
754         if ( _control_panel.getColorBranchesCb() != null ) {
755             _control_panel.getColorBranchesCb().setSelected( true );
756         }
757         for( final PreorderTreeIterator it = new PreorderTreeIterator( node ); it.hasNext(); ) {
758             it.next().getBranchData().setBranchColor( new BranchColor( c ) );
759         }
760         repaint();
761     }
762
763     final private void colorSubtree( final PhylogenyNode node ) {
764         Color intitial_color = null;
765         if ( getControlPanel().isColorBranches() && ( PhylogenyMethods.getBranchColorValue( node ) != null )
766                 && ( ( ( !node.isRoot() && ( node.getParent().getNumberOfDescendants() < 3 ) ) ) || ( node.isRoot() ) ) ) {
767             intitial_color = PhylogenyMethods.getBranchColorValue( node );
768         }
769         else {
770             intitial_color = getTreeColorSet().getBranchColor();
771         }
772         _color_chooser.setColor( intitial_color );
773         _color_chooser.setPreviewPanel( new JPanel() );
774         final JDialog dialog = JColorChooser
775                 .createDialog( this,
776                                "Subtree colorization",
777                                true,
778                                _color_chooser,
779                                new SubtreeColorizationActionListener( _color_chooser, node ),
780                                null );
781         dialog.setVisible( true );
782     }
783
784     final void confColor() {
785         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
786             return;
787         }
788         setWaitCursor();
789         AptxUtil.removeBranchColors( _phylogeny );
790         AptxUtil.colorPhylogenyAccordingToConfidenceValues( _phylogeny, this );
791         _control_panel.setColorBranches( true );
792         if ( _control_panel.getColorBranchesCb() != null ) {
793             _control_panel.getColorBranchesCb().setSelected( true );
794         }
795         setArrowCursor();
796         repaint();
797     }
798
799     final void colorRank( final String rank ) {
800         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
801             return;
802         }
803         setWaitCursor();
804         AptxUtil.removeBranchColors( _phylogeny );
805         final int colorizations = AptxUtil.colorPhylogenyAccordingToRanks( _phylogeny, rank, this );
806         if ( colorizations > 0 ) {
807             _control_panel.setColorBranches( true );
808             if ( _control_panel.getColorBranchesCb() != null ) {
809                 _control_panel.getColorBranchesCb().setSelected( true );
810             }
811             if ( _control_panel.getColorAccSpeciesCb() != null ) {
812                 _control_panel.getColorAccSpeciesCb().setSelected( false );
813             }
814             _options.setColorLabelsSameAsParentBranch( true );
815             _control_panel.repaint();
816         }
817         setArrowCursor();
818         repaint();
819         if ( colorizations > 0 ) {
820             String msg = "Taxonomy colorization via " + rank + " completed:\n";
821             if ( colorizations > 1 ) {
822                 msg += "colorized " + colorizations + " subtrees";
823             }
824             else {
825                 msg += "colorized one subtree";
826             }
827             JOptionPane.showMessageDialog( this,
828                                            msg,
829                                            "Taxonomy Colorization Completed (" + rank + ")",
830                                            JOptionPane.INFORMATION_MESSAGE );
831         }
832         else {
833             String msg = "Could not taxonomy colorize any subtree via " + rank + ".\n";
834             msg += "Possible solutions (given that suitable taxonomic information is present):\n";
835             msg += "select a different rank (e.g. phylum, genus, ...)\n";
836             msg += "  and/or\n";
837             msg += "execute:\n";
838             msg += "1. \"" + MainFrameApplication.OBTAIN_DETAILED_TAXONOMIC_INFORMATION + "\" (Tools)\n";
839             msg += "2. \"" + MainFrameApplication.INFER_ANCESTOR_TAXONOMIES + "\" (Analysis)";
840             JOptionPane.showMessageDialog( this, msg, "Taxonomy Colorization Failed", JOptionPane.WARNING_MESSAGE );
841         }
842     }
843
844     final private void copySubtree( final PhylogenyNode node ) {
845         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
846             errorMessageNoCutCopyPasteInUnrootedDisplay();
847             return;
848         }
849         setNodeInPreorderToNull();
850         setCutOrCopiedTree( _phylogeny.copy( node ) );
851         final List<PhylogenyNode> nodes = PhylogenyMethods.getAllDescendants( node );
852         final Set<Integer> node_ids = new HashSet<Integer>( nodes.size() );
853         for( final PhylogenyNode n : nodes ) {
854             node_ids.add( n.getId() );
855         }
856         node_ids.add( node.getId() );
857         setCopiedAndPastedNodes( node_ids );
858         repaint();
859     }
860
861     final private void cutSubtree( final PhylogenyNode node ) {
862         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
863             errorMessageNoCutCopyPasteInUnrootedDisplay();
864             return;
865         }
866         if ( node.isRoot() ) {
867             JOptionPane.showMessageDialog( this,
868                                            "Cannot cut entire tree as subtree",
869                                            "Attempt to cut entire tree",
870                                            JOptionPane.ERROR_MESSAGE );
871             return;
872         }
873         final String label = getASimpleTextRepresentationOfANode( node );
874         final int r = JOptionPane.showConfirmDialog( null,
875                                                      "Cut subtree" + label + "?",
876                                                      "Confirm Cutting of Subtree",
877                                                      JOptionPane.YES_NO_OPTION );
878         if ( r != JOptionPane.OK_OPTION ) {
879             return;
880         }
881         setNodeInPreorderToNull();
882         setCopiedAndPastedNodes( null );
883         setCutOrCopiedTree( _phylogeny.copy( node ) );
884         _phylogeny.deleteSubtree( node, true );
885         _phylogeny.clearHashIdToNodeMap();
886         _phylogeny.recalculateNumberOfExternalDescendants( true );
887         resetNodeIdToDistToLeafMap();
888         setEdited( true );
889         repaint();
890     }
891
892     final private void cycleColors() {
893         getMainPanel().getTreeColorSet().cycleColorScheme();
894         for( final TreePanel tree_panel : getMainPanel().getTreePanels() ) {
895             tree_panel.setBackground( getMainPanel().getTreeColorSet().getBackgroundColor() );
896         }
897     }
898
899     final void decreaseDomainStructureEvalueThreshold() {
900         if ( _domain_structure_e_value_thr_exp > -20 ) {
901             _domain_structure_e_value_thr_exp -= 1;
902         }
903     }
904
905     final private void decreaseOvSize() {
906         if ( ( getOvMaxWidth() > 20 ) && ( getOvMaxHeight() > 20 ) ) {
907             setOvMaxWidth( getOvMaxWidth() - 5 );
908             setOvMaxHeight( getOvMaxHeight() - 5 );
909             updateOvSettings();
910             getControlPanel().displayedPhylogenyMightHaveChanged( false );
911         }
912     }
913
914     final private void deleteNodeOrSubtree( final PhylogenyNode node ) {
915         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
916             errorMessageNoCutCopyPasteInUnrootedDisplay();
917             return;
918         }
919         if ( node.isRoot() ) {
920             JOptionPane.showMessageDialog( this,
921                                            "Cannot delete entire tree",
922                                            "Attempt to delete entire tree",
923                                            JOptionPane.ERROR_MESSAGE );
924             return;
925         }
926         final String label = getASimpleTextRepresentationOfANode( node );
927         final Object[] options = { "Node only", "Entire subtree", "Cancel" };
928         final int r = JOptionPane.showOptionDialog( this,
929                                                     "Delete" + label + "?",
930                                                     "Delete Node/Subtree",
931                                                     JOptionPane.CLOSED_OPTION,
932                                                     JOptionPane.QUESTION_MESSAGE,
933                                                     null,
934                                                     options,
935                                                     options[ 2 ] );
936         setNodeInPreorderToNull();
937         boolean node_only = true;
938         if ( r == 1 ) {
939             node_only = false;
940         }
941         else if ( r != 0 ) {
942             return;
943         }
944         if ( node_only ) {
945             PhylogenyMethods.removeNode( node, _phylogeny );
946         }
947         else {
948             _phylogeny.deleteSubtree( node, true );
949         }
950         _phylogeny.externalNodesHaveChanged();
951         _phylogeny.clearHashIdToNodeMap();
952         _phylogeny.recalculateNumberOfExternalDescendants( true );
953         resetNodeIdToDistToLeafMap();
954         setEdited( true );
955         repaint();
956     }
957
958     final private void displayNodePopupMenu( final PhylogenyNode node, final int x, final int y ) {
959         makePopupMenus( node );
960         _node_popup_menu.putClientProperty( NODE_POPMENU_NODE_CLIENT_PROPERTY, node );
961         _node_popup_menu.show( this, x, y );
962     }
963
964     final private void drawArc( final double x,
965                                 final double y,
966                                 final double width,
967                                 final double heigth,
968                                 final double start_angle,
969                                 final double arc_angle,
970                                 final Graphics2D g ) {
971         _arc.setArc( x, y, width, heigth, _180_OVER_PI * start_angle, _180_OVER_PI * arc_angle, Arc2D.OPEN );
972         g.draw( _arc );
973     }
974
975     final private void drawLine( final double x1, final double y1, final double x2, final double y2, final Graphics2D g ) {
976         if ( ( x1 == x2 ) && ( y1 == y2 ) ) {
977             return;
978         }
979         _line.setLine( x1, y1, x2, y2 );
980         g.draw( _line );
981     }
982
983     final private void drawOval( final double x,
984                                  final double y,
985                                  final double width,
986                                  final double heigth,
987                                  final Graphics2D g ) {
988         _ellipse.setFrame( x, y, width, heigth );
989         g.draw( _ellipse );
990     }
991
992     final private void drawOvalFilled( final double x,
993                                        final double y,
994                                        final double width,
995                                        final double heigth,
996                                        final Graphics2D g ) {
997         _ellipse.setFrame( x, y, width, heigth );
998         g.fill( _ellipse );
999     }
1000
1001     final private void drawRect( final float x, final float y, final float width, final float heigth, final Graphics2D g ) {
1002         _rectangle.setFrame( x, y, width, heigth );
1003         g.draw( _rectangle );
1004     }
1005
1006     final private void drawRectFilled( final double x,
1007                                        final double y,
1008                                        final double width,
1009                                        final double heigth,
1010                                        final Graphics2D g ) {
1011         _rectangle.setFrame( x, y, width, heigth );
1012         g.fill( _rectangle );
1013     }
1014
1015     final private void drawRectGradient( final double x,
1016                                          final double y,
1017                                          final double width,
1018                                          final double heigth,
1019                                          final Graphics2D g,
1020                                          final Color color_1,
1021                                          final Color color_2,
1022                                          final Color color_border ) {
1023         _rectangle.setFrame( x, y, width, heigth );
1024         g.setPaint( new GradientPaint( ( float ) x,
1025                                        ( float ) y,
1026                                        color_1,
1027                                        ( float ) ( x + width ),
1028                                        ( float ) ( y + heigth ),
1029                                        color_2,
1030                                        false ) );
1031         g.fill( _rectangle );
1032         if ( color_border != null ) {
1033             g.setPaint( color_border );
1034             g.draw( _rectangle );
1035         }
1036     }
1037
1038     final private void drawOvalGradient( final double x,
1039                                          final double y,
1040                                          final double width,
1041                                          final double heigth,
1042                                          final Graphics2D g,
1043                                          final Color color_1,
1044                                          final Color color_2,
1045                                          final Color color_border ) {
1046         _ellipse.setFrame( x, y, width, heigth );
1047         g.setPaint( new GradientPaint( ( float ) x,
1048                                        ( float ) y,
1049                                        color_1,
1050                                        ( float ) ( x + width ),
1051                                        ( float ) ( y + heigth ),
1052                                        color_2,
1053                                        false ) );
1054         g.fill( _ellipse );
1055         if ( color_border != null ) {
1056             g.setPaint( color_border );
1057             g.draw( _ellipse );
1058         }
1059     }
1060
1061     final private void errorMessageNoCutCopyPasteInUnrootedDisplay() {
1062         JOptionPane.showMessageDialog( this,
1063                                        "Cannot cut, copy, paste, add, or delete subtrees/nodes in unrooted display",
1064                                        "Attempt to cut/copy/paste/add/delete in unrooted display",
1065                                        JOptionPane.ERROR_MESSAGE );
1066     }
1067
1068     /**
1069      * Find the node, if any, at the given location
1070      * 
1071      * @param x
1072      * @param y
1073      * @return pointer to the node at x,y, null if not found
1074      */
1075     final PhylogenyNode findNode( final int x, final int y ) {
1076         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
1077             return null;
1078         }
1079         final int half_box_size_plus_wiggle = getOptions().getDefaultNodeShapeSize() / 2 + WIGGLE;
1080         for( final PhylogenyNodeIterator iter = _phylogeny.iteratorPostorder(); iter.hasNext(); ) {
1081             final PhylogenyNode node = iter.next();
1082             if ( ( _phylogeny.isRooted() || !node.isRoot() || ( node.getNumberOfDescendants() > 2 ) )
1083                     && ( ( node.getXcoord() - half_box_size_plus_wiggle ) <= x )
1084                     && ( ( node.getXcoord() + half_box_size_plus_wiggle ) >= x )
1085                     && ( ( node.getYcoord() - half_box_size_plus_wiggle ) <= y )
1086                     && ( ( node.getYcoord() + half_box_size_plus_wiggle ) >= y ) ) {
1087                 return node;
1088             }
1089         }
1090         return null;
1091     }
1092
1093     final private String getASimpleTextRepresentationOfANode( final PhylogenyNode node ) {
1094         final String tax = PhylogenyMethods.getSpecies( node );
1095         String label = node.getName();
1096         if ( !ForesterUtil.isEmpty( label ) && !ForesterUtil.isEmpty( tax ) ) {
1097             label = label + " " + tax;
1098         }
1099         else if ( !ForesterUtil.isEmpty( tax ) ) {
1100             label = tax;
1101         }
1102         else {
1103             label = "";
1104         }
1105         if ( !ForesterUtil.isEmpty( label ) ) {
1106             label = " [" + label + "]";
1107         }
1108         return label;
1109     }
1110
1111     final Configuration getConfiguration() {
1112         return _configuration;
1113     }
1114
1115     final ControlPanel getControlPanel() {
1116         return _control_panel;
1117     }
1118
1119     final private Set<Integer> getCopiedAndPastedNodes() {
1120         return getMainPanel().getCopiedAndPastedNodes();
1121     }
1122
1123     final private Phylogeny getCutOrCopiedTree() {
1124         return getMainPanel().getCutOrCopiedTree();
1125     }
1126
1127     final int getDomainStructureEvalueThreshold() {
1128         return _domain_structure_e_value_thr_exp;
1129     }
1130
1131     final Set<Integer> getFoundNodes() {
1132         return _found_nodes;
1133     }
1134
1135     final private float getLastDragPointX() {
1136         return _last_drag_point_x;
1137     }
1138
1139     final private float getLastDragPointY() {
1140         return _last_drag_point_y;
1141     }
1142
1143     final int getLongestExtNodeInfo() {
1144         return _longest_ext_node_info;
1145     }
1146
1147     final public MainPanel getMainPanel() {
1148         return _main_panel;
1149     }
1150
1151     final private short getMaxBranchesToLeaf( final PhylogenyNode node ) {
1152         if ( !_nodeid_dist_to_leaf.containsKey( node.getId() ) ) {
1153             final short m = PhylogenyMethods.calculateMaxBranchesToLeaf( node );
1154             _nodeid_dist_to_leaf.put( node.getId(), m );
1155             return m;
1156         }
1157         else {
1158             return _nodeid_dist_to_leaf.get( node.getId() );
1159         }
1160     }
1161
1162     final private double getMaxDistanceToRoot() {
1163         if ( _max_distance_to_root < 0 ) {
1164             recalculateMaxDistanceToRoot();
1165         }
1166         return _max_distance_to_root;
1167     }
1168
1169     final Options getOptions() {
1170         if ( _options == null ) {
1171             _options = getControlPanel().getOptions();
1172         }
1173         return _options;
1174     }
1175
1176     final private float getOvMaxHeight() {
1177         return _ov_max_height;
1178     }
1179
1180     final private float getOvMaxWidth() {
1181         return _ov_max_width;
1182     }
1183
1184     final Rectangle2D getOvRectangle() {
1185         return _ov_rectangle;
1186     }
1187
1188     final Rectangle getOvVirtualRectangle() {
1189         return _ov_virtual_rectangle;
1190     }
1191
1192     final private float getOvXcorrectionFactor() {
1193         return _ov_x_correction_factor;
1194     }
1195
1196     final private float getOvXDistance() {
1197         return _ov_x_distance;
1198     }
1199
1200     final private int getOvXPosition() {
1201         return _ov_x_position;
1202     }
1203
1204     final private float getOvYDistance() {
1205         return _ov_y_distance;
1206     }
1207
1208     final private int getOvYPosition() {
1209         return _ov_y_position;
1210     }
1211
1212     final private int getOvYStart() {
1213         return _ov_y_start;
1214     }
1215
1216     /**
1217      * Get a pointer to the phylogeny 
1218      * 
1219      * @return a pointer to the phylogeny
1220      */
1221     public final Phylogeny getPhylogeny() {
1222         return _phylogeny;
1223     }
1224
1225     final PHYLOGENY_GRAPHICS_TYPE getPhylogenyGraphicsType() {
1226         return _graphics_type;
1227     }
1228
1229     final private double getScaleDistance() {
1230         return _scale_distance;
1231     }
1232
1233     final private String getScaleLabel() {
1234         return _scale_label;
1235     }
1236
1237     final double getStartingAngle() {
1238         return _urt_starting_angle;
1239     }
1240
1241     /**
1242      * Find a color for this species name.
1243      * 
1244      * @param species
1245      * @return the species color
1246      */
1247     final Color getTaxonomyBasedColor( final PhylogenyNode node ) {
1248         if ( node.getNodeData().isHasTaxonomy() ) {
1249             return calculateTaxonomyBasedColor( node.getNodeData().getTaxonomy() );
1250         }
1251         // return non-colorized color
1252         return getTreeColorSet().getTaxonomyColor();
1253     }
1254
1255     /**
1256      * @return pointer to colorset for tree drawing
1257      */
1258     final TreeColorSet getTreeColorSet() {
1259         return getMainPanel().getTreeColorSet();
1260     }
1261
1262     final File getTreeFile() {
1263         return _treefile;
1264     }
1265
1266     final private TreeFontSet getTreeFontSet() {
1267         return getMainPanel().getTreeFontSet();
1268     }
1269
1270     final private float getUrtFactor() {
1271         return _urt_factor;
1272     }
1273
1274     final private float getUrtFactorOv() {
1275         return _urt_factor_ov;
1276     }
1277
1278     final float getXcorrectionFactor() {
1279         return _x_correction_factor;
1280     }
1281
1282     final float getXdistance() {
1283         return _x_distance;
1284     }
1285
1286     final float getYdistance() {
1287         return _y_distance;
1288     }
1289
1290     final private void handleClickToAction( final NodeClickAction action, final PhylogenyNode node ) {
1291         switch ( action ) {
1292             case SHOW_DATA:
1293                 showNodeFrame( node );
1294                 break;
1295             case COLLAPSE:
1296                 collapse( node );
1297                 break;
1298             case REROOT:
1299                 reRoot( node );
1300                 break;
1301             case SUBTREE:
1302                 subTree( node );
1303                 break;
1304             case SWAP:
1305                 swap( node );
1306                 break;
1307             case COLOR_SUBTREE:
1308                 colorSubtree( node );
1309                 break;
1310             case OPEN_SEQ_WEB:
1311                 openSeqWeb( node );
1312                 break;
1313             case BLAST:
1314                 blast( node );
1315                 break;
1316             case OPEN_TAX_WEB:
1317                 openTaxWeb( node );
1318                 break;
1319             case CUT_SUBTREE:
1320                 cutSubtree( node );
1321                 break;
1322             case COPY_SUBTREE:
1323                 copySubtree( node );
1324                 break;
1325             case PASTE_SUBTREE:
1326                 pasteSubtree( node );
1327                 break;
1328             case DELETE_NODE_OR_SUBTREE:
1329                 deleteNodeOrSubtree( node );
1330                 break;
1331             case ADD_NEW_NODE:
1332                 addEmptyNode( node );
1333                 break;
1334             case EDIT_NODE_DATA:
1335                 showNodeEditFrame( node );
1336                 break;
1337             case SORT_DESCENDENTS:
1338                 sortDescendants( node );
1339                 break;
1340             case GET_EXT_DESC_DATA:
1341                 getExtDescNodeData( node );
1342                 break;
1343             default:
1344                 throw new IllegalArgumentException( "unknown action: " + action );
1345         }
1346     }
1347
1348     private void getExtDescNodeData( final PhylogenyNode node ) {
1349         System.out.println( node.toString() );
1350     }
1351
1352     final void increaseDomainStructureEvalueThreshold() {
1353         if ( _domain_structure_e_value_thr_exp < 3 ) {
1354             _domain_structure_e_value_thr_exp += 1;
1355         }
1356     }
1357
1358     final private void increaseOvSize() {
1359         if ( ( getOvMaxWidth() < getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect().getWidth() / 2 )
1360                 && ( getOvMaxHeight() < getMainPanel().getCurrentScrollPane().getViewport().getVisibleRect()
1361                         .getHeight() / 2 ) ) {
1362             setOvMaxWidth( getOvMaxWidth() + 5 );
1363             setOvMaxHeight( getOvMaxHeight() + 5 );
1364             updateOvSettings();
1365             getControlPanel().displayedPhylogenyMightHaveChanged( false );
1366         }
1367     }
1368
1369     final void inferCommonPartOfScientificNames() {
1370         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
1371             return;
1372         }
1373         setWaitCursor();
1374         AptxUtil.inferCommonPartOfScientificNames( _phylogeny );
1375         setArrowCursor();
1376         repaint();
1377     }
1378
1379     final private void init() {
1380         _color_chooser = new JColorChooser();
1381         _rollover_popup = new JTextArea();
1382         _rollover_popup.setFont( POPUP_FONT );
1383         resetNodeIdToDistToLeafMap();
1384         setTextAntialias();
1385         setTreeFile( null );
1386         setEdited( false );
1387         initializeOvSettings();
1388         setStartingAngle( TWO_PI * 3 / 4 );
1389         final ImageLoader il = new ImageLoader( this );
1390         new Thread( il ).start();
1391     }
1392
1393     final private void initializeOvSettings() {
1394         setOvMaxHeight( getConfiguration().getOvMaxHeight() );
1395         setOvMaxWidth( getConfiguration().getOvMaxWidth() );
1396     }
1397
1398     final void initNodeData() {
1399         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
1400             return;
1401         }
1402         double max_original_domain_structure_width = 0.0;
1403         for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
1404             if ( node.getNodeData().isHasSequence()
1405                     && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1406                 RenderableDomainArchitecture rds = null;
1407                 if ( !( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) ) {
1408                     rds = new RenderableDomainArchitecture( node.getNodeData().getSequence().getDomainArchitecture(),
1409                                                             getConfiguration() );
1410                     node.getNodeData().getSequence().setDomainArchitecture( rds );
1411                 }
1412                 else {
1413                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
1414                 }
1415                 if ( getControlPanel().isShowDomainArchitectures() ) {
1416                     final double dsw = rds.getOriginalSize().getWidth();
1417                     if ( dsw > max_original_domain_structure_width ) {
1418                         max_original_domain_structure_width = dsw;
1419                     }
1420                 }
1421             }
1422         }
1423         if ( getControlPanel().isShowDomainArchitectures() ) {
1424             final double ds_factor_width = _domain_structure_width / max_original_domain_structure_width;
1425             for( final PhylogenyNode node : _phylogeny.getExternalNodes() ) {
1426                 if ( node.getNodeData().isHasSequence()
1427                         && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
1428                     final RenderableDomainArchitecture rds = ( RenderableDomainArchitecture ) node.getNodeData()
1429                             .getSequence().getDomainArchitecture();
1430                     rds.setRenderingFactorWidth( ds_factor_width );
1431                     rds.setParameter( _domain_structure_e_value_thr_exp );
1432                 }
1433             }
1434         }
1435     }
1436
1437     final boolean inOv( final MouseEvent e ) {
1438         return ( ( e.getX() > getVisibleRect().x + getOvXPosition() + 1 )
1439                 && ( e.getX() < getVisibleRect().x + getOvXPosition() + getOvMaxWidth() - 1 )
1440                 && ( e.getY() > getVisibleRect().y + getOvYPosition() + 1 ) && ( e.getY() < getVisibleRect().y
1441                 + getOvYPosition() + getOvMaxHeight() - 1 ) );
1442     }
1443
1444     final boolean inOvRectangle( final MouseEvent e ) {
1445         return ( ( e.getX() >= getOvRectangle().getX() - 1 )
1446                 && ( e.getX() <= getOvRectangle().getX() + getOvRectangle().getWidth() + 1 )
1447                 && ( e.getY() >= getOvRectangle().getY() - 1 ) && ( e.getY() <= getOvRectangle().getY()
1448                 + getOvRectangle().getHeight() + 1 ) );
1449     }
1450
1451     final private boolean inOvVirtualRectangle( final int x, final int y ) {
1452         return ( ( x >= getOvVirtualRectangle().x - 1 )
1453                 && ( x <= getOvVirtualRectangle().x + getOvVirtualRectangle().width + 1 )
1454                 && ( y >= getOvVirtualRectangle().y - 1 ) && ( y <= getOvVirtualRectangle().y
1455                 + getOvVirtualRectangle().height + 1 ) );
1456     }
1457
1458     final private boolean inOvVirtualRectangle( final MouseEvent e ) {
1459         return ( inOvVirtualRectangle( e.getX(), e.getY() ) );
1460     }
1461
1462     final boolean isApplet() {
1463         return getMainPanel() instanceof MainPanelApplets;
1464     }
1465
1466     final private boolean isCanBlast( final PhylogenyNode node ) {
1467         return ( node.getNodeData().isHasSequence()
1468                 && ( ( ( node.getNodeData().getSequence().getAccession() != null ) && !ForesterUtil.isEmpty( node
1469                         .getNodeData().getSequence().getAccession().getValue() ) )
1470                         || !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) || !ForesterUtil
1471                         .isEmpty( node.getNodeData().getSequence().getMolecularSequence() ) ) && Blast
1472                 .isContainsQueryForBlast( node ) );
1473     }
1474
1475     final boolean isCanCollapse() {
1476         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
1477     }
1478
1479     final boolean isCanColorSubtree() {
1480         return ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
1481     }
1482
1483     final boolean isCanCopy() {
1484         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
1485     }
1486
1487     final boolean isCanCut( final PhylogenyNode node ) {
1488         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() && !node
1489                 .isRoot() );
1490     }
1491
1492     final boolean isCanDelete() {
1493         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable() );
1494     }
1495
1496     final private boolean isCanOpenSeqWeb( final PhylogenyNode node ) {
1497         if ( node.getNodeData().isHasSequence()
1498                 && ( node.getNodeData().getSequence().getAccession() != null )
1499                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() )
1500                 && !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getValue() )
1501                 && getConfiguration().isHasWebLink( node.getNodeData().getSequence().getAccession().getSource()
1502                         .toLowerCase() ) ) {
1503             return true;
1504         }
1505         return false;
1506     }
1507
1508     final private boolean isCanOpenTaxWeb( final PhylogenyNode node ) {
1509         if ( node.getNodeData().isHasTaxonomy()
1510                 && ( ( ( node.getNodeData().getTaxonomy().getIdentifier() != null )
1511                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getProvider() )
1512                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && getConfiguration()
1513                         .isHasWebLink( node.getNodeData().getTaxonomy().getIdentifier().getProvider().toLowerCase() ) )
1514                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getScientificName() ) )
1515                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getTaxonomyCode() ) )
1516                         || ( !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getCommonName() ) ) || ( ( node
1517                         .getNodeData().getTaxonomy().getIdentifier() != null )
1518                         && !ForesterUtil.isEmpty( node.getNodeData().getTaxonomy().getIdentifier().getValue() ) && node
1519                         .getNodeData().getTaxonomy().getIdentifier().getValue().startsWith( "http://" ) ) ) ) {
1520             return true;
1521         }
1522         else {
1523             return false;
1524         }
1525     }
1526
1527     final boolean isCanPaste() {
1528         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && getOptions().isEditable()
1529                 && ( getCutOrCopiedTree() != null ) && !getCutOrCopiedTree().isEmpty() );
1530     }
1531
1532     final boolean isCanReroot() {
1533         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && ( _subtree_index < 1 ) );
1534     }
1535
1536     final boolean isCanSubtree( final PhylogenyNode node ) {
1537         return ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) && !node.isExternal() && ( !node
1538                 .isRoot() || ( _subtree_index > 0 ) ) );
1539     }
1540
1541     final boolean isEdited() {
1542         return _edited;
1543     }
1544
1545     final private boolean isInFoundNodes( final PhylogenyNode node ) {
1546         return ( ( getFoundNodes() != null ) && getFoundNodes().contains( node.getId() ) );
1547     }
1548
1549     final private boolean isInOv() {
1550         return _in_ov;
1551     }
1552
1553     final boolean isInOvRect() {
1554         return _in_ov_rect;
1555     }
1556
1557     final private boolean isNodeDataInvisible( final PhylogenyNode node ) {
1558         int y_dist = 40;
1559         if ( getControlPanel().isShowTaxonomyImages() ) {
1560             y_dist = 40 + ( int ) getYdistance();
1561         }
1562         return ( ( node.getYcoord() < getVisibleRect().getMinY() - y_dist )
1563                 || ( node.getYcoord() > getVisibleRect().getMaxY() + y_dist ) || ( ( node.getParent() != null ) && ( node
1564                 .getParent().getXcoord() > getVisibleRect().getMaxX() ) ) );
1565     }
1566
1567     final private boolean isNodeDataInvisibleUnrootedCirc( final PhylogenyNode node ) {
1568         return ( ( node.getYcoord() < getVisibleRect().getMinY() - 20 )
1569                 || ( node.getYcoord() > getVisibleRect().getMaxY() + 20 )
1570                 || ( node.getXcoord() < getVisibleRect().getMinX() - 20 ) || ( node.getXcoord() > getVisibleRect()
1571                 .getMaxX() + 20 ) );
1572     }
1573
1574     final private boolean isNonLinedUpCladogram() {
1575         return getOptions().getCladogramType() == CLADOGRAM_TYPE.NON_LINED_UP;
1576     }
1577
1578     final boolean isOvOn() {
1579         return _ov_on;
1580     }
1581
1582     final boolean isPhyHasBranchLengths() {
1583         return _phy_has_branch_lengths;
1584     }
1585
1586     final private boolean isUniformBranchLengthsForCladogram() {
1587         return getOptions().getCladogramType() == CLADOGRAM_TYPE.TOTAL_NODE_SUM_DEP;
1588     }
1589
1590     final private void keyPressedCalls( final KeyEvent e ) {
1591         if ( isOvOn() && ( getMousePosition() != null ) && ( getMousePosition().getLocation() != null ) ) {
1592             if ( inOvVirtualRectangle( getMousePosition().x, getMousePosition().y ) ) {
1593                 if ( !isInOvRect() ) {
1594                     setInOvRect( true );
1595                 }
1596             }
1597             else if ( isInOvRect() ) {
1598                 setInOvRect( false );
1599             }
1600         }
1601         if ( e.getModifiersEx() == InputEvent.CTRL_DOWN_MASK ) {
1602             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
1603                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
1604                 getMainPanel().getTreeFontSet().mediumFonts();
1605                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1606             }
1607             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
1608                 getMainPanel().getTreeFontSet().decreaseFontSize();
1609                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1610             }
1611             else if ( plusPressed( e.getKeyCode() ) ) {
1612                 getMainPanel().getTreeFontSet().increaseFontSize();
1613                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( true );
1614             }
1615         }
1616         else {
1617             if ( ( e.getKeyCode() == KeyEvent.VK_DELETE ) || ( e.getKeyCode() == KeyEvent.VK_HOME )
1618                     || ( e.getKeyCode() == KeyEvent.VK_F ) ) {
1619                 getControlPanel().showWhole();
1620             }
1621             else if ( ( e.getKeyCode() == KeyEvent.VK_UP ) || ( e.getKeyCode() == KeyEvent.VK_DOWN )
1622                     || ( e.getKeyCode() == KeyEvent.VK_LEFT ) || ( e.getKeyCode() == KeyEvent.VK_RIGHT ) ) {
1623                 if ( e.getModifiersEx() == InputEvent.SHIFT_DOWN_MASK ) {
1624                     if ( e.getKeyCode() == KeyEvent.VK_UP ) {
1625                         getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
1626                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1627                     }
1628                     else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1629                         getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
1630                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1631                     }
1632                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1633                         getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
1634                                                                    Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1635                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1636                     }
1637                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1638                         getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
1639                                                                   Constants.WHEEL_ZOOM_IN_FACTOR );
1640                         getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1641                     }
1642                 }
1643                 else {
1644                     final int d = 80;
1645                     int dx = 0;
1646                     int dy = -d;
1647                     if ( e.getKeyCode() == KeyEvent.VK_DOWN ) {
1648                         dy = d;
1649                     }
1650                     else if ( e.getKeyCode() == KeyEvent.VK_LEFT ) {
1651                         dx = -d;
1652                         dy = 0;
1653                     }
1654                     else if ( e.getKeyCode() == KeyEvent.VK_RIGHT ) {
1655                         dx = d;
1656                         dy = 0;
1657                     }
1658                     final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1659                     scroll_position.x = scroll_position.x + dx;
1660                     scroll_position.y = scroll_position.y + dy;
1661                     if ( scroll_position.x <= 0 ) {
1662                         scroll_position.x = 0;
1663                     }
1664                     else {
1665                         final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1666                                 - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1667                         if ( scroll_position.x >= max_x ) {
1668                             scroll_position.x = max_x;
1669                         }
1670                     }
1671                     if ( scroll_position.y <= 0 ) {
1672                         scroll_position.y = 0;
1673                     }
1674                     else {
1675                         final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1676                                 - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1677                         if ( scroll_position.y >= max_y ) {
1678                             scroll_position.y = max_y;
1679                         }
1680                     }
1681                     repaint();
1682                     getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1683                 }
1684             }
1685             else if ( ( e.getKeyCode() == KeyEvent.VK_SUBTRACT ) || ( e.getKeyCode() == KeyEvent.VK_MINUS ) ) {
1686                 getMainPanel().getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
1687                 getMainPanel().getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
1688                                                            Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
1689                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1690             }
1691             else if ( plusPressed( e.getKeyCode() ) ) {
1692                 getMainPanel().getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
1693                                                           Constants.WHEEL_ZOOM_IN_FACTOR );
1694                 getMainPanel().getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
1695                 getMainPanel().getControlPanel().displayedPhylogenyMightHaveChanged( false );
1696             }
1697             else if ( e.getKeyCode() == KeyEvent.VK_S ) {
1698                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1699                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1700                     setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
1701                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1702                 }
1703             }
1704             else if ( e.getKeyCode() == KeyEvent.VK_A ) {
1705                 if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
1706                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
1707                     setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
1708                     if ( getStartingAngle() < 0 ) {
1709                         setStartingAngle( TWO_PI + getStartingAngle() );
1710                     }
1711                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
1712                 }
1713             }
1714             else if ( e.getKeyCode() == KeyEvent.VK_D ) {
1715                 boolean selected = false;
1716                 if ( getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.HORIZONTAL ) {
1717                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.RADIAL );
1718                     selected = true;
1719                 }
1720                 else {
1721                     getOptions().setNodeLabelDirection( NODE_LABEL_DIRECTION.HORIZONTAL );
1722                 }
1723                 if ( getMainPanel().getMainFrame() == null ) {
1724                     // Must be "E" applet version.
1725                     final ArchaeopteryxE ae = ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet();
1726                     if ( ae.getlabelDirectionCbmi() != null ) {
1727                         ae.getlabelDirectionCbmi().setSelected( selected );
1728                     }
1729                 }
1730                 else {
1731                     getMainPanel().getMainFrame().getlabelDirectionCbmi().setSelected( selected );
1732                 }
1733                 repaint();
1734             }
1735             else if ( e.getKeyCode() == KeyEvent.VK_X ) {
1736                 switchDisplaygetPhylogenyGraphicsType();
1737                 repaint();
1738             }
1739             else if ( e.getKeyCode() == KeyEvent.VK_C ) {
1740                 cycleColors();
1741                 repaint();
1742             }
1743             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_O ) ) {
1744                 MainFrame.cycleOverview( getOptions(), this );
1745                 repaint();
1746             }
1747             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_I ) ) {
1748                 increaseOvSize();
1749             }
1750             else if ( getOptions().isShowOverview() && isOvOn() && ( e.getKeyCode() == KeyEvent.VK_U ) ) {
1751                 decreaseOvSize();
1752             }
1753             e.consume();
1754         }
1755     }
1756
1757     final private void makePopupMenus( final PhylogenyNode node ) {
1758         _node_popup_menu = new JPopupMenu();
1759         final List<String> clickto_names = _main_panel.getControlPanel().getSingleClickToNames();
1760         _node_popup_menu_items = new JMenuItem[ clickto_names.size() ];
1761         for( int i = 0; i < clickto_names.size(); i++ ) {
1762             final String title = clickto_names.get( i );
1763             _node_popup_menu_items[ i ] = new JMenuItem( title );
1764             if ( title.equals( Configuration.clickto_options[ Configuration.open_seq_web ][ 0 ] ) ) {
1765                 _node_popup_menu_items[ i ].setEnabled( isCanOpenSeqWeb( node ) );
1766             }
1767             else if ( title.equals( Configuration.clickto_options[ Configuration.open_tax_web ][ 0 ] ) ) {
1768                 _node_popup_menu_items[ i ].setEnabled( isCanOpenTaxWeb( node ) );
1769             }
1770             else if ( title.equals( Configuration.clickto_options[ Configuration.blast ][ 0 ] ) ) {
1771                 _node_popup_menu_items[ i ].setEnabled( isCanBlast( node ) );
1772             }
1773             else if ( title.equals( Configuration.clickto_options[ Configuration.delete_subtree_or_node ][ 0 ] ) ) {
1774                 if ( !getOptions().isEditable() ) {
1775                     continue;
1776                 }
1777                 _node_popup_menu_items[ i ].setEnabled( isCanDelete() );
1778             }
1779             else if ( title.equals( Configuration.clickto_options[ Configuration.cut_subtree ][ 0 ] ) ) {
1780                 if ( !getOptions().isEditable() ) {
1781                     continue;
1782                 }
1783                 _node_popup_menu_items[ i ].setEnabled( isCanCut( node ) );
1784             }
1785             else if ( title.equals( Configuration.clickto_options[ Configuration.copy_subtree ][ 0 ] ) ) {
1786                 if ( !getOptions().isEditable() ) {
1787                     continue;
1788                 }
1789                 _node_popup_menu_items[ i ].setEnabled( isCanCopy() );
1790             }
1791             else if ( title.equals( Configuration.clickto_options[ Configuration.paste_subtree ][ 0 ] ) ) {
1792                 if ( !getOptions().isEditable() ) {
1793                     continue;
1794                 }
1795                 _node_popup_menu_items[ i ].setEnabled( isCanPaste() );
1796             }
1797             else if ( title.equals( Configuration.clickto_options[ Configuration.edit_node_data ][ 0 ] ) ) {
1798                 if ( !getOptions().isEditable() ) {
1799                     continue;
1800                 }
1801             }
1802             else if ( title.equals( Configuration.clickto_options[ Configuration.add_new_node ][ 0 ] ) ) {
1803                 if ( !getOptions().isEditable() ) {
1804                     continue;
1805                 }
1806             }
1807             else if ( title.equals( Configuration.clickto_options[ Configuration.reroot ][ 0 ] ) ) {
1808                 _node_popup_menu_items[ i ].setEnabled( isCanReroot() );
1809             }
1810             else if ( title.equals( Configuration.clickto_options[ Configuration.collapse_uncollapse ][ 0 ] ) ) {
1811                 _node_popup_menu_items[ i ].setEnabled( ( isCanCollapse() && !node.isExternal() ) );
1812             }
1813             else if ( title.equals( Configuration.clickto_options[ Configuration.color_subtree ][ 0 ] ) ) {
1814                 _node_popup_menu_items[ i ].setEnabled( isCanColorSubtree() );
1815             }
1816             else if ( title.equals( Configuration.clickto_options[ Configuration.subtree ][ 0 ] ) ) {
1817                 _node_popup_menu_items[ i ].setEnabled( isCanSubtree( node ) );
1818             }
1819             else if ( title.equals( Configuration.clickto_options[ Configuration.swap ][ 0 ] ) ) {
1820                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() == 2 );
1821             }
1822             else if ( title.equals( Configuration.clickto_options[ Configuration.sort_descendents ][ 0 ] ) ) {
1823                 _node_popup_menu_items[ i ].setEnabled( node.getNumberOfDescendants() > 1 );
1824             }
1825             _node_popup_menu_items[ i ].addActionListener( this );
1826             _node_popup_menu.add( _node_popup_menu_items[ i ] );
1827         }
1828     }
1829
1830     final void midpointRoot() {
1831         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
1832             return;
1833         }
1834         if ( !_phylogeny.isRerootable() ) {
1835             JOptionPane.showMessageDialog( this,
1836                                            "This is not rerootable",
1837                                            "Not rerootable",
1838                                            JOptionPane.WARNING_MESSAGE );
1839             return;
1840         }
1841         setNodeInPreorderToNull();
1842         setWaitCursor();
1843         PhylogenyMethods.midpointRoot( _phylogeny );
1844         resetNodeIdToDistToLeafMap();
1845         setArrowCursor();
1846         repaint();
1847     }
1848
1849     final void mouseClicked( final MouseEvent e ) {
1850         if ( getOptions().isShowOverview() && isOvOn() && isInOv() ) {
1851             final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
1852             final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
1853             double x = ( e.getX() - getVisibleRect().x - getOvXPosition() - getOvRectangle().getWidth() / 2.0 )
1854                     * w_ratio;
1855             double y = ( e.getY() - getVisibleRect().y - getOvYPosition() - getOvRectangle().getHeight() / 2.0 )
1856                     * h_ratio;
1857             if ( x < 0 ) {
1858                 x = 0;
1859             }
1860             if ( y < 0 ) {
1861                 y = 0;
1862             }
1863             final double max_x = getWidth() - getVisibleRect().width;
1864             final double max_y = getHeight() - getVisibleRect().height;
1865             if ( x > max_x ) {
1866                 x = max_x;
1867             }
1868             if ( y > max_y ) {
1869                 y = max_y;
1870             }
1871             getMainPanel().getCurrentScrollPane().getViewport()
1872                     .setViewPosition( new Point( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ) ) );
1873             setInOvRect( true );
1874             repaint();
1875         }
1876         else {
1877             final PhylogenyNode node = findNode( e.getX(), e.getY() );
1878             if ( node != null ) {
1879                 if ( !node.isRoot() && node.getParent().isCollapse() ) {
1880                     return;
1881                 }
1882                 _highlight_node = node;
1883                 // Check if shift key is down
1884                 if ( ( e.getModifiers() & InputEvent.SHIFT_MASK ) != 0 ) {
1885                     // Yes, so add to _found_nodes
1886                     if ( getFoundNodes() == null ) {
1887                         setFoundNodes( new HashSet<Integer>() );
1888                     }
1889                     getFoundNodes().add( node.getId() );
1890                     // Check if control key is down
1891                 }
1892                 else if ( ( e.getModifiers() & InputEvent.CTRL_MASK ) != 0 ) {
1893                     // Yes, so pop-up menu
1894                     displayNodePopupMenu( node, e.getX(), e.getY() );
1895                     // Handle unadorned click
1896                 }
1897                 else {
1898                     // Check for right mouse button
1899                     if ( e.getModifiers() == 4 ) {
1900                         displayNodePopupMenu( node, e.getX(), e.getY() );
1901                     }
1902                     else {
1903                         // if not in _found_nodes, clear _found_nodes
1904                         handleClickToAction( _control_panel.getActionWhenNodeClicked(), node );
1905                     }
1906                 }
1907             }
1908             else {
1909                 // no node was clicked
1910                 _highlight_node = null;
1911             }
1912         }
1913         repaint();
1914     }
1915
1916     final void mouseDragInBrowserPanel( final MouseEvent e ) {
1917         setCursor( MOVE_CURSOR );
1918         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1919         scroll_position.x -= ( e.getX() - getLastDragPointX() );
1920         scroll_position.y -= ( e.getY() - getLastDragPointY() );
1921         if ( scroll_position.x < 0 ) {
1922             scroll_position.x = 0;
1923         }
1924         else {
1925             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1926                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1927             if ( scroll_position.x > max_x ) {
1928                 scroll_position.x = max_x;
1929             }
1930         }
1931         if ( scroll_position.y < 0 ) {
1932             scroll_position.y = 0;
1933         }
1934         else {
1935             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1936                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1937             if ( scroll_position.y > max_y ) {
1938                 scroll_position.y = max_y;
1939             }
1940         }
1941         if ( isOvOn() || getOptions().isShowScale() ) {
1942             repaint();
1943         }
1944         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1945     }
1946
1947     final void mouseDragInOvRectangle( final MouseEvent e ) {
1948         setCursor( HAND_CURSOR );
1949         final double w_ratio = getVisibleRect().width / getOvRectangle().getWidth();
1950         final double h_ratio = getVisibleRect().height / getOvRectangle().getHeight();
1951         final Point scroll_position = getMainPanel().getCurrentScrollPane().getViewport().getViewPosition();
1952         double dx = ( w_ratio * e.getX() - w_ratio * getLastDragPointX() );
1953         double dy = ( h_ratio * e.getY() - h_ratio * getLastDragPointY() );
1954         scroll_position.x = ForesterUtil.roundToInt( scroll_position.x + dx );
1955         scroll_position.y = ForesterUtil.roundToInt( scroll_position.y + dy );
1956         if ( scroll_position.x <= 0 ) {
1957             scroll_position.x = 0;
1958             dx = 0;
1959         }
1960         else {
1961             final int max_x = getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getMaximum()
1962                     - getMainPanel().getCurrentScrollPane().getHorizontalScrollBar().getVisibleAmount();
1963             if ( scroll_position.x >= max_x ) {
1964                 dx = 0;
1965                 scroll_position.x = max_x;
1966             }
1967         }
1968         if ( scroll_position.y <= 0 ) {
1969             dy = 0;
1970             scroll_position.y = 0;
1971         }
1972         else {
1973             final int max_y = getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getMaximum()
1974                     - getMainPanel().getCurrentScrollPane().getVerticalScrollBar().getVisibleAmount();
1975             if ( scroll_position.y >= max_y ) {
1976                 dy = 0;
1977                 scroll_position.y = max_y;
1978             }
1979         }
1980         repaint();
1981         getMainPanel().getCurrentScrollPane().getViewport().setViewPosition( scroll_position );
1982         setLastMouseDragPointX( ( float ) ( e.getX() + dx ) );
1983         setLastMouseDragPointY( ( float ) ( e.getY() + dy ) );
1984     }
1985
1986     final void mouseMoved( final MouseEvent e ) {
1987         requestFocusInWindow();
1988         if ( getControlPanel().isNodeDescPopup() ) {
1989             if ( _node_desc_popup != null ) {
1990                 _node_desc_popup.hide();
1991                 _node_desc_popup = null;
1992             }
1993         }
1994         if ( getOptions().isShowOverview() && isOvOn() ) {
1995             if ( inOvVirtualRectangle( e ) ) {
1996                 if ( !isInOvRect() ) {
1997                     setInOvRect( true );
1998                     repaint();
1999                 }
2000             }
2001             else {
2002                 if ( isInOvRect() ) {
2003                     setInOvRect( false );
2004                     repaint();
2005                 }
2006             }
2007         }
2008         if ( inOv( e ) && getOptions().isShowOverview() && isOvOn() ) {
2009             if ( !isInOv() ) {
2010                 setInOv( true );
2011             }
2012         }
2013         else {
2014             if ( isInOv() ) {
2015                 setInOv( false );
2016             }
2017             final PhylogenyNode node = findNode( e.getX(), e.getY() );
2018             if ( ( node != null ) && ( node.isRoot() || !node.getParent().isCollapse() ) ) {
2019                 // cursor is over a tree node
2020                 if ( ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.CUT_SUBTREE )
2021                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.COPY_SUBTREE )
2022                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.PASTE_SUBTREE )
2023                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.DELETE_NODE_OR_SUBTREE )
2024                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.REROOT )
2025                         || ( getControlPanel().getActionWhenNodeClicked() == NodeClickAction.ADD_NEW_NODE ) ) {
2026                     setCursor( CUT_CURSOR );
2027                 }
2028                 else {
2029                     setCursor( HAND_CURSOR );
2030                     if ( getControlPanel().isNodeDescPopup() ) {
2031                         showNodeDataPopup( e, node );
2032                     }
2033                 }
2034             }
2035             else {
2036                 setCursor( ARROW_CURSOR );
2037             }
2038         }
2039     }
2040
2041     final void mouseReleasedInBrowserPanel( final MouseEvent e ) {
2042         setCursor( ARROW_CURSOR );
2043     }
2044
2045     final public void mouseWheelMoved( final MouseWheelEvent e ) {
2046         final int notches = e.getWheelRotation();
2047         if ( inOvVirtualRectangle( e ) ) {
2048             if ( !isInOvRect() ) {
2049                 setInOvRect( true );
2050                 repaint();
2051             }
2052         }
2053         else {
2054             if ( isInOvRect() ) {
2055                 setInOvRect( false );
2056                 repaint();
2057             }
2058         }
2059         if ( e.isControlDown() ) {
2060             if ( notches < 0 ) {
2061                 getTreeFontSet().increaseFontSize();
2062                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
2063             }
2064             else {
2065                 getTreeFontSet().decreaseFontSize();
2066                 getControlPanel().displayedPhylogenyMightHaveChanged( true );
2067             }
2068         }
2069         else if ( e.isShiftDown() ) {
2070             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
2071                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
2072                 if ( notches < 0 ) {
2073                     for( int i = 0; i < ( -notches ); ++i ) {
2074                         setStartingAngle( ( getStartingAngle() % TWO_PI ) + ANGLE_ROTATION_UNIT );
2075                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2076                     }
2077                 }
2078                 else {
2079                     for( int i = 0; i < notches; ++i ) {
2080                         setStartingAngle( ( getStartingAngle() % TWO_PI ) - ANGLE_ROTATION_UNIT );
2081                         if ( getStartingAngle() < 0 ) {
2082                             setStartingAngle( TWO_PI + getStartingAngle() );
2083                         }
2084                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2085                     }
2086                 }
2087             }
2088             else {
2089                 if ( notches < 0 ) {
2090                     for( int i = 0; i < ( -notches ); ++i ) {
2091                         getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2092                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2093                     }
2094                 }
2095                 else {
2096                     for( int i = 0; i < notches; ++i ) {
2097                         getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2098                         getControlPanel().displayedPhylogenyMightHaveChanged( false );
2099                     }
2100                 }
2101             }
2102         }
2103         else {
2104             if ( notches < 0 ) {
2105                 for( int i = 0; i < ( -notches ); ++i ) {
2106                     getControlPanel().zoomInX( Constants.WHEEL_ZOOM_IN_FACTOR,
2107                                                Constants.WHEEL_ZOOM_IN_X_CORRECTION_FACTOR );
2108                     getControlPanel().zoomInY( Constants.WHEEL_ZOOM_IN_FACTOR );
2109                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2110                 }
2111             }
2112             else {
2113                 for( int i = 0; i < notches; ++i ) {
2114                     getControlPanel().zoomOutY( Constants.WHEEL_ZOOM_OUT_FACTOR );
2115                     getControlPanel().zoomOutX( Constants.WHEEL_ZOOM_OUT_FACTOR,
2116                                                 Constants.WHEEL_ZOOM_OUT_X_CORRECTION_FACTOR );
2117                     getControlPanel().displayedPhylogenyMightHaveChanged( false );
2118                 }
2119             }
2120         }
2121         requestFocus();
2122         requestFocusInWindow();
2123         requestFocus();
2124     }
2125
2126     final void multiplyUrtFactor( final float f ) {
2127         _urt_factor *= f;
2128     }
2129
2130     final JApplet obtainApplet() {
2131         return ( ( MainPanelApplets ) getMainPanel() ).getApplet();
2132     }
2133
2134     final private void openSeqWeb( final PhylogenyNode node ) {
2135         if ( !isCanOpenSeqWeb( node ) ) {
2136             cannotOpenBrowserWarningMessage( "sequence" );
2137             return;
2138         }
2139         String uri_str = null;
2140         final Sequence seq = node.getNodeData().getSequence();
2141         final String source = seq.getAccession().getSource().toLowerCase();
2142         String url;
2143         if ( source.toLowerCase().equals( "ncbi" ) ) {
2144             url = Constants.NCBI_ALL_DATABASE_SEARCH;
2145         }
2146         else {
2147             final WebLink weblink = getConfiguration().getWebLink( source );
2148             url = weblink.getUrl().toString();
2149         }
2150         try {
2151             uri_str = url + URLEncoder.encode( seq.getAccession().getValue(), ForesterConstants.UTF8 );
2152         }
2153         catch ( final UnsupportedEncodingException e ) {
2154             AptxUtil.showErrorMessage( this, e.toString() );
2155             e.printStackTrace();
2156         }
2157         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2158             try {
2159                 JApplet applet = null;
2160                 if ( isApplet() ) {
2161                     applet = obtainApplet();
2162                 }
2163                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_seq" );
2164             }
2165             catch ( final IOException e ) {
2166                 AptxUtil.showErrorMessage( this, e.toString() );
2167                 e.printStackTrace();
2168             }
2169             catch ( final URISyntaxException e ) {
2170                 AptxUtil.showErrorMessage( this, e.toString() );
2171                 e.printStackTrace();
2172             }
2173         }
2174         else {
2175             cannotOpenBrowserWarningMessage( "sequence" );
2176         }
2177     }
2178
2179     final private void openTaxWeb( final PhylogenyNode node ) {
2180         if ( !isCanOpenTaxWeb( node ) ) {
2181             cannotOpenBrowserWarningMessage( "taxonomic" );
2182             return;
2183         }
2184         String uri_str = null;
2185         final Taxonomy tax = node.getNodeData().getTaxonomy();
2186         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() )
2187                 && getConfiguration().isHasWebLink( tax.getIdentifier().getProvider().toLowerCase() ) ) {
2188             final String type = tax.getIdentifier().getProvider().toLowerCase();
2189             final WebLink weblink = getConfiguration().getWebLink( type );
2190             try {
2191                 uri_str = weblink.getUrl() + URLEncoder.encode( tax.getIdentifier().getValue(), ForesterConstants.UTF8 );
2192             }
2193             catch ( final UnsupportedEncodingException e ) {
2194                 AptxUtil.showErrorMessage( this, e.toString() );
2195                 e.printStackTrace();
2196             }
2197         }
2198         else if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() )
2199                 && tax.getIdentifier().getValue().startsWith( "http://" ) ) {
2200             try {
2201                 uri_str = new URI( tax.getIdentifier().getValue() ).toString();
2202             }
2203             catch ( final URISyntaxException e ) {
2204                 AptxUtil.showErrorMessage( this, e.toString() );
2205                 uri_str = null;
2206                 e.printStackTrace();
2207             }
2208         }
2209         else if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
2210             try {
2211                 uri_str = "http://www.eol.org/search?q="
2212                         + URLEncoder.encode( tax.getScientificName(), ForesterConstants.UTF8 );
2213             }
2214             catch ( final UnsupportedEncodingException e ) {
2215                 AptxUtil.showErrorMessage( this, e.toString() );
2216                 e.printStackTrace();
2217             }
2218         }
2219         else if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
2220             try {
2221                 uri_str = "http://www.uniprot.org/taxonomy/?query="
2222                         + URLEncoder.encode( tax.getTaxonomyCode(), ForesterConstants.UTF8 );
2223             }
2224             catch ( final UnsupportedEncodingException e ) {
2225                 AptxUtil.showErrorMessage( this, e.toString() );
2226                 e.printStackTrace();
2227             }
2228         }
2229         else if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
2230             try {
2231                 uri_str = "http://www.eol.org/search?q="
2232                         + URLEncoder.encode( tax.getCommonName(), ForesterConstants.UTF8 );
2233             }
2234             catch ( final UnsupportedEncodingException e ) {
2235                 AptxUtil.showErrorMessage( this, e.toString() );
2236                 e.printStackTrace();
2237             }
2238         }
2239         if ( !ForesterUtil.isEmpty( uri_str ) ) {
2240             try {
2241                 JApplet applet = null;
2242                 if ( isApplet() ) {
2243                     applet = obtainApplet();
2244                 }
2245                 AptxUtil.launchWebBrowser( new URI( uri_str ), isApplet(), applet, "_aptx_tax" );
2246             }
2247             catch ( final IOException e ) {
2248                 AptxUtil.showErrorMessage( this, e.toString() );
2249                 e.printStackTrace();
2250             }
2251             catch ( final URISyntaxException e ) {
2252                 AptxUtil.showErrorMessage( this, e.toString() );
2253                 e.printStackTrace();
2254             }
2255         }
2256         else {
2257             cannotOpenBrowserWarningMessage( "taxonomic" );
2258         }
2259     }
2260
2261     final void paintBranchCircular( final PhylogenyNode p,
2262                                     final PhylogenyNode c,
2263                                     final Graphics2D g,
2264                                     final boolean radial_labels,
2265                                     final boolean to_pdf,
2266                                     final boolean to_graphics_file ) {
2267         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2268         final double root_x = _root.getXcoord();
2269         final double root_y = _root.getYcoord();
2270         final double dx = root_x - p.getXcoord();
2271         final double dy = root_y - p.getYcoord();
2272         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2273         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2274         assignGraphicsForBranchWithColorForParentBranch( c, false, g, to_pdf, to_graphics_file );
2275         if ( ( c.isFirstChildNode() || c.isLastChildNode() )
2276                 && ( ( Math.abs( parent_radius * arc ) > 1.5 ) || to_pdf || to_graphics_file ) ) {
2277             final double r2 = 2.0 * parent_radius;
2278             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2279         }
2280         drawLine( c.getXcoord(),
2281                   c.getYcoord(),
2282                   root_x + ( Math.cos( angle ) * parent_radius ),
2283                   root_y + ( Math.sin( angle ) * parent_radius ),
2284                   g );
2285         paintNodeBox( c.getXcoord(), c.getYcoord(), c, g, to_pdf, to_graphics_file, isInFoundNodes( c ) );
2286         if ( c.isExternal() ) {
2287             final boolean is_in_found_nodes = isInFoundNodes( c );
2288             if ( ( _dynamic_hiding_factor > 1 ) && !is_in_found_nodes
2289                     && ( _urt_nodeid_index_map.get( c.getId() ) % _dynamic_hiding_factor != 1 ) ) {
2290                 return;
2291             }
2292             paintNodeDataUnrootedCirc( g, c, to_pdf, to_graphics_file, radial_labels, 0, is_in_found_nodes );
2293         }
2294     }
2295
2296     final void paintBranchCircularLite( final PhylogenyNode p, final PhylogenyNode c, final Graphics2D g ) {
2297         final double angle = _urt_nodeid_angle_map.get( c.getId() );
2298         final double root_x = _root.getXSecondary();
2299         final double root_y = _root.getYSecondary();
2300         final double dx = root_x - p.getXSecondary();
2301         final double dy = root_y - p.getYSecondary();
2302         final double arc = ( _urt_nodeid_angle_map.get( p.getId() ) ) - angle;
2303         final double parent_radius = Math.sqrt( dx * dx + dy * dy );
2304         g.setColor( getTreeColorSet().getOvColor() );
2305         if ( ( c.isFirstChildNode() || c.isLastChildNode() ) && ( Math.abs( arc ) > 0.02 ) ) {
2306             final double r2 = 2.0 * parent_radius;
2307             drawArc( root_x - parent_radius, root_y - parent_radius, r2, r2, ( -angle - arc ), arc, g );
2308         }
2309         drawLine( c.getXSecondary(),
2310                   c.getYSecondary(),
2311                   root_x + ( Math.cos( angle ) * parent_radius ),
2312                   root_y + ( Math.sin( angle ) * parent_radius ),
2313                   g );
2314         if ( isInFoundNodes( c ) ) {
2315             g.setColor( getTreeColorSet().getFoundColor() );
2316             drawRectFilled( c.getXSecondary() - 1, c.getYSecondary() - 1, 3, 3, g );
2317         }
2318     }
2319
2320     final private void paintBranchLength( final Graphics2D g,
2321                                           final PhylogenyNode node,
2322                                           final boolean to_pdf,
2323                                           final boolean to_graphics_file ) {
2324         g.setFont( getTreeFontSet().getSmallFont() );
2325         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2326             g.setColor( Color.BLACK );
2327         }
2328         else {
2329             g.setColor( getTreeColorSet().getBranchLengthColor() );
2330         }
2331         if ( !node.isRoot() ) {
2332             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2333                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2334                         .getXcoord() + EURO_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2335             }
2336             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2337                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2338                         .getXcoord() + ROUNDED_D, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2339             }
2340             else {
2341                 TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), node.getParent()
2342                         .getXcoord() + 3, node.getYcoord() - getTreeFontSet()._small_max_descent, g );
2343             }
2344         }
2345         else {
2346             TreePanel.drawString( FORMATTER_BRANCH_LENGTH.format( node.getDistanceToParent() ), 3, node.getYcoord()
2347                     - getTreeFontSet()._small_max_descent, g );
2348         }
2349     }
2350
2351     final private void paintBranchLite( final Graphics2D g,
2352                                         final float x1,
2353                                         final float x2,
2354                                         final float y1,
2355                                         final float y2,
2356                                         final PhylogenyNode node ) {
2357         g.setColor( getTreeColorSet().getOvColor() );
2358         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2359             drawLine( x1, y1, x2, y2, g );
2360         }
2361         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2362             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2363             ( g ).draw( _quad_curve );
2364         }
2365         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2366             final float dx = x2 - x1;
2367             final float dy = y2 - y1;
2368             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2369                     + ( dy * 0.8f ), x2, y2 );
2370             ( g ).draw( _cubic_curve );
2371         }
2372         else {
2373             final float x2a = x2;
2374             final float x1a = x1;
2375             // draw the vertical line
2376             if ( node.isFirstChildNode() || node.isLastChildNode() ) {
2377                 drawLine( x1, y1, x1, y2, g );
2378             }
2379             // draw the horizontal line
2380             drawLine( x1a, y2, x2a, y2, g );
2381         }
2382     }
2383
2384     /**
2385      * Paint a branch which consists of a vertical and a horizontal bar
2386      * @param is_ind_found_nodes 
2387      */
2388     final private void paintBranchRectangular( final Graphics2D g,
2389                                                final float x1,
2390                                                final float x2,
2391                                                final float y1,
2392                                                final float y2,
2393                                                final PhylogenyNode node,
2394                                                final boolean to_pdf,
2395                                                final boolean to_graphics_file ) {
2396         assignGraphicsForBranchWithColorForParentBranch( node, false, g, to_pdf, to_graphics_file );
2397         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR ) {
2398             drawLine( x1, y1, x2, y2, g );
2399         }
2400         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CONVEX ) {
2401             _quad_curve.setCurve( x1, y1, x1, y2, x2, y2 );
2402             g.draw( _quad_curve );
2403         }
2404         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CURVED ) {
2405             final float dx = x2 - x1;
2406             final float dy = y2 - y1;
2407             _cubic_curve.setCurve( x1, y1, x1 + ( dx * 0.4f ), y1 + ( dy * 0.2f ), x1 + ( dx * 0.6f ), y1
2408                     + ( dy * 0.8f ), x2, y2 );
2409             g.draw( _cubic_curve );
2410         }
2411         else {
2412             final float x2a = x2;
2413             final float x1a = x1;
2414             float y2_r = 0;
2415             if ( node.isFirstChildNode() || node.isLastChildNode()
2416                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
2417                     || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2418                 if ( !to_graphics_file
2419                         && !to_pdf
2420                         && ( ( ( y2 < getVisibleRect().getMinY() - 20 ) && ( y1 < getVisibleRect().getMinY() - 20 ) ) || ( ( y2 > getVisibleRect()
2421                                 .getMaxY() + 20 ) && ( y1 > getVisibleRect().getMaxY() + 20 ) ) ) ) {
2422                     // Do nothing.
2423                 }
2424                 else {
2425                     if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2426                         float x2c = x1 + EURO_D;
2427                         if ( x2c > x2a ) {
2428                             x2c = x2a;
2429                         }
2430                         drawLine( x1, y1, x2c, y2, g );
2431                     }
2432                     else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2433                         if ( y2 > y1 ) {
2434                             y2_r = y2 - ROUNDED_D;
2435                             if ( y2_r < y1 ) {
2436                                 y2_r = y1;
2437                             }
2438                             drawLine( x1, y1, x1, y2_r, g );
2439                         }
2440                         else {
2441                             y2_r = y2 + ROUNDED_D;
2442                             if ( y2_r > y1 ) {
2443                                 y2_r = y1;
2444                             }
2445                             drawLine( x1, y1, x1, y2_r, g );
2446                         }
2447                     }
2448                     else {
2449                         drawLine( x1, y1, x1, y2, g );
2450                     }
2451                 }
2452             }
2453             // draw the horizontal line
2454             if ( !to_graphics_file && !to_pdf
2455                     && ( ( y2 < getVisibleRect().getMinY() - 20 ) || ( y2 > getVisibleRect().getMaxY() + 20 ) ) ) {
2456                 return;
2457             }
2458             float x1_r = 0;
2459             if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( node ) == 1 ) ) {
2460                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2461                     x1_r = x1a + ROUNDED_D;
2462                     if ( x1_r < x2a ) {
2463                         drawLine( x1_r, y2, x2a, y2, g );
2464                     }
2465                 }
2466                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2467                     final float x1c = x1a + EURO_D;
2468                     if ( x1c < x2a ) {
2469                         drawLine( x1c, y2, x2a, y2, g );
2470                     }
2471                 }
2472                 else {
2473                     drawLine( x1a, y2, x2a, y2, g );
2474                 }
2475             }
2476             else {
2477                 final double w = PhylogenyMethods.getBranchWidthValue( node );
2478                 if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2479                     x1_r = x1a + ROUNDED_D;
2480                     if ( x1_r < x2a ) {
2481                         drawRectFilled( x1_r, y2 - ( w / 2 ), x2a - x1_r, w, g );
2482                     }
2483                 }
2484                 else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2485                     final float x1c = x1a + EURO_D;
2486                     if ( x1c < x2a ) {
2487                         drawRectFilled( x1c, y2 - ( w / 2 ), x2a - x1c, w, g );
2488                     }
2489                 }
2490                 else {
2491                     drawRectFilled( x1a, y2 - ( w / 2 ), x2a - x1a, w, g );
2492                 }
2493             }
2494             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
2495                 if ( x1_r > x2a ) {
2496                     x1_r = x2a;
2497                 }
2498                 if ( y2 > y2_r ) {
2499                     final double diff = y2 - y2_r;
2500                     _arc.setArc( x1, y2_r - diff, 2 * ( x1_r - x1 ), 2 * diff, 180, 90, Arc2D.OPEN );
2501                 }
2502                 else {
2503                     _arc.setArc( x1, y2, 2 * ( x1_r - x1 ), 2 * ( y2_r - y2 ), 90, 90, Arc2D.OPEN );
2504                 }
2505                 g.draw( _arc );
2506             }
2507         }
2508         if ( node.isExternal() ) {
2509             paintNodeBox( x2, y2, node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
2510         }
2511     }
2512
2513     final void paintCircular( final Phylogeny phy,
2514                               final double starting_angle,
2515                               final int center_x,
2516                               final int center_y,
2517                               final int radius,
2518                               final Graphics2D g,
2519                               final boolean to_pdf,
2520                               final boolean to_graphics_file ) {
2521         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes() - _collapsed_external_nodeid_set.size();
2522         System.out.println( "# collapsed external = " + _collapsed_external_nodeid_set.size() );
2523         _root = phy.getRoot();
2524         _root.setXcoord( center_x );
2525         _root.setYcoord( center_y );
2526         final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
2527         double current_angle = starting_angle;
2528         int i = 0;
2529         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2530             final PhylogenyNode n = it.next();
2531             if ( !n.isCollapse() ) {
2532                 n.setXcoord( ( float ) ( center_x + ( radius * Math.cos( current_angle ) ) ) );
2533                 n.setYcoord( ( float ) ( center_y + ( radius * Math.sin( current_angle ) ) ) );
2534                 _urt_nodeid_angle_map.put( n.getId(), current_angle );
2535                 _urt_nodeid_index_map.put( n.getId(), i++ );
2536                 current_angle += ( TWO_PI / circ_num_ext_nodes );
2537             }
2538             else {
2539                 //TODO remove me
2540                 System.out.println( "is collapse" + n.getName() );
2541             }
2542         }
2543         paintCirculars( phy.getRoot(), phy, center_x, center_y, radius, radial_labels, g, to_pdf, to_graphics_file );
2544         paintNodeBox( _root.getXcoord(), _root.getYcoord(), _root, g, to_pdf, to_graphics_file, isInFoundNodes( _root ) );
2545     }
2546
2547     void updateSetOfCollapsedExternalNodes() {
2548         final Phylogeny phy = getPhylogeny();
2549         _collapsed_external_nodeid_set.clear();
2550         if ( phy != null ) {
2551             E: for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2552                 final PhylogenyNode ext_node = it.next();
2553                 PhylogenyNode n = ext_node;
2554                 while ( !n.isRoot() ) {
2555                     if ( n.isCollapse() ) {
2556                         _collapsed_external_nodeid_set.add( ext_node.getId() );
2557                         ext_node.setCollapse( true );
2558                         continue E;
2559                     }
2560                     n = n.getParent();
2561                 }
2562             }
2563         }
2564     }
2565
2566     final void paintCircularLite( final Phylogeny phy,
2567                                   final double starting_angle,
2568                                   final int center_x,
2569                                   final int center_y,
2570                                   final int radius,
2571                                   final Graphics2D g ) {
2572         final int circ_num_ext_nodes = phy.getNumberOfExternalNodes();
2573         _root = phy.getRoot();
2574         _root.setXSecondary( center_x );
2575         _root.setYSecondary( center_y );
2576         double current_angle = starting_angle;
2577         for( final PhylogenyNodeIterator it = phy.iteratorExternalForward(); it.hasNext(); ) {
2578             final PhylogenyNode n = it.next();
2579             n.setXSecondary( ( float ) ( center_x + radius * Math.cos( current_angle ) ) );
2580             n.setYSecondary( ( float ) ( center_y + radius * Math.sin( current_angle ) ) );
2581             _urt_nodeid_angle_map.put( n.getId(), current_angle );
2582             current_angle += ( TWO_PI / circ_num_ext_nodes );
2583         }
2584         paintCircularsLite( phy.getRoot(), phy, center_x, center_y, radius, g );
2585     }
2586
2587     final private double paintCirculars( final PhylogenyNode n,
2588                                          final Phylogeny phy,
2589                                          final float center_x,
2590                                          final float center_y,
2591                                          final double radius,
2592                                          final boolean radial_labels,
2593                                          final Graphics2D g,
2594                                          final boolean to_pdf,
2595                                          final boolean to_graphics_file ) {
2596         if ( n.isExternal() || n.isCollapse() ) { //~~circ collapse
2597             if ( !_urt_nodeid_angle_map.containsKey( n.getId() ) ) {
2598                 System.out.println( "no " + n + " =====>>>>>>> ERROR!" );//TODO
2599             }
2600             return _urt_nodeid_angle_map.get( n.getId() );
2601         }
2602         else {
2603             final List<PhylogenyNode> descs = n.getDescendants();
2604             double sum = 0;
2605             for( final PhylogenyNode desc : descs ) {
2606                 sum += paintCirculars( desc,
2607                                        phy,
2608                                        center_x,
2609                                        center_y,
2610                                        radius,
2611                                        radial_labels,
2612                                        g,
2613                                        to_pdf,
2614                                        to_graphics_file );
2615             }
2616             double r = 0;
2617             if ( !n.isRoot() ) {
2618                 r = 1 - ( ( ( double ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2619             }
2620             final double theta = sum / descs.size();
2621             n.setXcoord( ( float ) ( center_x + r * radius * Math.cos( theta ) ) );
2622             n.setYcoord( ( float ) ( center_y + r * radius * Math.sin( theta ) ) );
2623             _urt_nodeid_angle_map.put( n.getId(), theta );
2624             for( final PhylogenyNode desc : descs ) {
2625                 paintBranchCircular( n, desc, g, radial_labels, to_pdf, to_graphics_file );
2626             }
2627             return theta;
2628         }
2629     }
2630
2631     final private void paintCircularsLite( final PhylogenyNode n,
2632                                            final Phylogeny phy,
2633                                            final int center_x,
2634                                            final int center_y,
2635                                            final int radius,
2636                                            final Graphics2D g ) {
2637         if ( n.isExternal() ) {
2638             return;
2639         }
2640         else {
2641             final List<PhylogenyNode> descs = n.getDescendants();
2642             for( final PhylogenyNode desc : descs ) {
2643                 paintCircularsLite( desc, phy, center_x, center_y, radius, g );
2644             }
2645             float r = 0;
2646             if ( !n.isRoot() ) {
2647                 r = 1 - ( ( ( float ) _circ_max_depth - PhylogenyMethods.calculateDepth( n ) ) / _circ_max_depth );
2648             }
2649             final double theta = _urt_nodeid_angle_map.get( n.getId() );
2650             n.setXSecondary( ( float ) ( center_x + radius * r * Math.cos( theta ) ) );
2651             n.setYSecondary( ( float ) ( center_y + radius * r * Math.sin( theta ) ) );
2652             for( final PhylogenyNode desc : descs ) {
2653                 paintBranchCircularLite( n, desc, g );
2654             }
2655         }
2656     }
2657
2658     final private void paintCollapsedNode( final Graphics2D g,
2659                                            final PhylogenyNode node,
2660                                            final boolean to_graphics_file,
2661                                            final boolean to_pdf,
2662                                            final boolean is_in_found_nodes ) {
2663         Color c = null;
2664         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2665             c = Color.BLACK;
2666         }
2667         else if ( is_in_found_nodes ) {
2668             c = getTreeColorSet().getFoundColor();
2669         }
2670         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
2671             c = getTaxonomyBasedColor( node );
2672         }
2673         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
2674                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
2675             c = PhylogenyMethods.getBranchColorValue( node );
2676         }
2677         else {
2678             c = getTreeColorSet().getCollapseFillColor();
2679         }
2680         double d = node.getAllExternalDescendants().size();
2681         if ( d > 1000 ) {
2682             d = ( 3 * _y_distance ) / 3;
2683         }
2684         else {
2685             d = ( Math.log10( d ) * _y_distance ) / 2.5;
2686         }
2687         final int box_size = getOptions().getDefaultNodeShapeSize();
2688         if ( d < box_size ) {
2689             d = box_size;
2690         }
2691         _polygon.reset();
2692         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() - box_size ),
2693                            ForesterUtil.roundToInt( node.getYcoord() ) );
2694         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2695                            ForesterUtil.roundToInt( node.getYcoord() - d ) );
2696         _polygon.addPoint( ForesterUtil.roundToInt( node.getXcoord() + box_size ),
2697                            ForesterUtil.roundToInt( node.getYcoord() + d ) );
2698         if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2699             g.setColor( c );
2700             g.fillPolygon( _polygon );
2701         }
2702         else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
2703             g.setColor( getBackground() );
2704             g.fillPolygon( _polygon );
2705             g.setColor( c );
2706             g.drawPolygon( _polygon );
2707         }
2708         else if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2709             g.setPaint( new GradientPaint( node.getXcoord() - box_size, node.getYcoord(), getBackground(), ( node
2710                     .getXcoord() + box_size ), ( float ) ( node.getYcoord() - d ), c, false ) );
2711             g.fill( _polygon );
2712             g.setPaint( c );
2713             g.draw( _polygon );
2714         }
2715         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
2716     }
2717
2718     @Override
2719     final public void paintComponent( final Graphics g ) {
2720         // Dimension currentSize = getSize();
2721         //  if ( offscreenImage == null || !currentSize.equals( offscreenDimension ) ) {
2722         // call the 'java.awt.Component.createImage(...)' method to get an
2723         // image
2724         //   offscreenImage = createImage( currentSize.width, currentSize.height );
2725         //  offscreenGraphics = offscreenImage.getGraphics();
2726         //  offscreenDimension = currentSize;
2727         // }
2728         // super.paintComponent( g ); //why?
2729         //final Graphics2D g2d = ( Graphics2D ) offscreenGraphics;
2730         final Graphics2D g2d = ( Graphics2D ) g;
2731         g2d.setRenderingHints( _rendering_hints );
2732         paintPhylogeny( g2d, false, false, 0, 0, 0, 0 );
2733         //g.drawImage( offscreenImage, 0, 0, this );
2734     }
2735
2736     @Override
2737     public void update( final Graphics g ) {
2738         paint( g );
2739     }
2740
2741     final private void paintConfidenceValues( final Graphics2D g,
2742                                               final PhylogenyNode node,
2743                                               final boolean to_pdf,
2744                                               final boolean to_graphics_file ) {
2745         final List<Confidence> confidences = node.getBranchData().getConfidences();
2746         //        if ( confidences.size() == 1 ) {
2747         //            final double value = node.getBranchData().getConfidence( 0 ).getValue();
2748         //            if ( ( value == Confidence.CONFIDENCE_DEFAULT_VALUE ) || ( value < getOptions().getMinConfidenceValue() ) ) {
2749         //                return;
2750         //            }
2751         //            conf_str = FORMATTER_CONFIDENCE.format( value );
2752         //        }
2753         //        else if ( confidences.size() > 1 ) {
2754         boolean one_ok = false;
2755         boolean not_first = false;
2756         Collections.sort( confidences );
2757         final StringBuilder sb = new StringBuilder();
2758         String conf_str = "";
2759         for( final Confidence confidence : confidences ) {
2760             final double value = confidence.getValue();
2761             if ( value != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2762                 if ( value >= getOptions().getMinConfidenceValue() ) {
2763                     one_ok = true;
2764                 }
2765                 if ( not_first ) {
2766                     sb.append( "/" );
2767                 }
2768                 else {
2769                     not_first = true;
2770                 }
2771                 sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( value, getOptions()
2772                         .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2773                 if ( getOptions().isShowConfidenceStddev() ) {
2774                     if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
2775                         sb.append( "(" );
2776                         sb.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getStandardDeviation(),
2777                                                                                     getOptions()
2778                                                                                             .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
2779                         sb.append( ")" );
2780                     }
2781                 }
2782             }
2783             //}
2784             if ( one_ok ) {
2785                 conf_str = sb.toString();
2786             }
2787         }
2788         if ( conf_str.length() > 0 ) {
2789             final double parent_x = node.getParent().getXcoord();
2790             double x = node.getXcoord();
2791             g.setFont( getTreeFontSet().getSmallFont() );
2792             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) {
2793                 x += EURO_D;
2794             }
2795             else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) {
2796                 x += ROUNDED_D;
2797             }
2798             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2799                 g.setColor( Color.BLACK );
2800             }
2801             else {
2802                 g.setColor( getTreeColorSet().getConfidenceColor() );
2803             }
2804             TreePanel
2805                     .drawString( conf_str,
2806                                  parent_x
2807                                          + ( ( x - parent_x - getTreeFontSet()._fm_small.stringWidth( conf_str ) ) / 2 ),
2808                                  ( node.getYcoord() + getTreeFontSet()._small_max_ascent ) - 1,
2809                                  g );
2810         }
2811     }
2812
2813     final private void paintFoundNode( final int x, final int y, final Graphics2D g ) {
2814         final int box_size = getOptions().getDefaultNodeShapeSize();
2815         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2816         g.setColor( getTreeColorSet().getFoundColor() );
2817         g.fillRect( x - half_box_size, y - half_box_size, box_size, box_size );
2818     }
2819
2820     final private void paintGainedAndLostCharacters( final Graphics2D g,
2821                                                      final PhylogenyNode node,
2822                                                      final String gained,
2823                                                      final String lost ) {
2824         if ( node.getParent() != null ) {
2825             final double parent_x = node.getParent().getXcoord();
2826             final double x = node.getXcoord();
2827             g.setFont( getTreeFontSet().getLargeFont() );
2828             g.setColor( getTreeColorSet().getGainedCharactersColor() );
2829             if ( Constants.SPECIAL_CUSTOM ) {
2830                 g.setColor( Color.BLUE );
2831             }
2832             TreePanel
2833                     .drawString( gained,
2834                                  parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( gained ) ) / 2 ),
2835                                  ( node.getYcoord() - getTreeFontSet()._fm_large.getMaxDescent() ),
2836                                  g );
2837             g.setColor( getTreeColorSet().getLostCharactersColor() );
2838             TreePanel.drawString( lost,
2839                                   parent_x + ( ( x - parent_x - getTreeFontSet()._fm_large.stringWidth( lost ) ) / 2 ),
2840                                   ( node.getYcoord() + getTreeFontSet()._fm_large.getMaxAscent() ),
2841                                   g );
2842         }
2843     }
2844
2845     /**
2846      * Draw a box at the indicated node.
2847      * 
2848      * @param x
2849      * @param y
2850      * @param node
2851      * @param g
2852      */
2853     final private void paintNodeBox( final double x,
2854                                      final double y,
2855                                      final PhylogenyNode node,
2856                                      final Graphics2D g,
2857                                      final boolean to_pdf,
2858                                      final boolean to_graphics_file,
2859                                      final boolean is_in_found_nodes ) {
2860         if ( node.isCollapse() ) {
2861             return;
2862         }
2863         // if this node should be highlighted, do so
2864         if ( ( _highlight_node == node ) && !to_pdf && !to_graphics_file ) {
2865             g.setColor( getTreeColorSet().getFoundColor() );
2866             drawOval( x - 8, y - 8, 16, 16, g );
2867             drawOval( x - 9, y - 8, 17, 17, g );
2868             drawOval( x - 9, y - 9, 18, 18, g );
2869         }
2870         if ( is_in_found_nodes ) {
2871             paintFoundNode( ForesterUtil.roundToInt( x ), ForesterUtil.roundToInt( y ), g );
2872         }
2873         else {
2874             Color outline_color = null;
2875             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2876                 outline_color = Color.BLACK;
2877             }
2878             else if ( getControlPanel().isEvents() && AptxUtil.isHasAssignedEvent( node ) ) {
2879                 final Event event = node.getNodeData().getEvent();
2880                 if ( event.isDuplication() ) {
2881                     outline_color = getTreeColorSet().getDuplicationBoxColor();
2882                 }
2883                 else if ( event.isSpeciation() ) {
2884                     outline_color = getTreeColorSet().getSpecBoxColor();
2885                 }
2886                 else if ( event.isSpeciationOrDuplication() ) {
2887                     outline_color = getTreeColorSet().getDuplicationOrSpeciationColor();
2888                 }
2889             }
2890             else if ( getOptions().isTaxonomyColorizeNodeShapes() ) {
2891                 outline_color = getTaxonomyBasedColor( node );
2892             }
2893             else {
2894                 outline_color = getGraphicsForNodeBoxWithColorForParentBranch( node );
2895                 if ( to_pdf && ( outline_color == getTreeColorSet().getBranchColor() ) ) {
2896                     outline_color = getTreeColorSet().getBranchColorForPdf();
2897                 }
2898             }
2899             final int box_size = getOptions().getDefaultNodeShapeSize();
2900             final int half_box_size = box_size / 2;
2901             if ( getOptions().isShowDefaultNodeShapes() || ( getControlPanel().isEvents() && node.isHasAssignedEvent() ) ) {
2902                 if ( getOptions().getDefaultNodeShape() == NodeShape.CIRCLE ) {
2903                     if ( getOptions().getDefaultNodeFill() == NodeFill.GRADIENT ) {
2904                         drawOvalGradient( x - half_box_size,
2905                                           y - half_box_size,
2906                                           box_size,
2907                                           box_size,
2908                                           g,
2909                                           to_pdf ? Color.WHITE : outline_color,
2910                                           to_pdf ? outline_color : getBackground(),
2911                                           outline_color );
2912                     }
2913                     else if ( getOptions().getDefaultNodeFill() == NodeFill.NONE ) {
2914                         Color background = getBackground();
2915                         if ( to_pdf ) {
2916                             background = Color.WHITE;
2917                         }
2918                         drawOvalGradient( x - half_box_size,
2919                                           y - half_box_size,
2920                                           box_size,
2921                                           box_size,
2922                                           g,
2923                                           background,
2924                                           background,
2925                                           outline_color );
2926                     }
2927                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2928                         g.setColor( outline_color );
2929                         drawOvalFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2930                     }
2931                 }
2932                 else if ( getOptions().getDefaultNodeShape() == NodeVisualization.NodeShape.RECTANGLE ) {
2933                     if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.GRADIENT ) {
2934                         drawRectGradient( x - half_box_size,
2935                                           y - half_box_size,
2936                                           box_size,
2937                                           box_size,
2938                                           g,
2939                                           to_pdf ? Color.WHITE : outline_color,
2940                                           to_pdf ? outline_color : getBackground(),
2941                                           outline_color );
2942                     }
2943                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.NONE ) {
2944                         Color background = getBackground();
2945                         if ( to_pdf ) {
2946                             background = Color.WHITE;
2947                         }
2948                         drawRectGradient( x - half_box_size,
2949                                           y - half_box_size,
2950                                           box_size,
2951                                           box_size,
2952                                           g,
2953                                           background,
2954                                           background,
2955                                           outline_color );
2956                     }
2957                     else if ( getOptions().getDefaultNodeFill() == NodeVisualization.NodeFill.SOLID ) {
2958                         g.setColor( outline_color );
2959                         drawRectFilled( x - half_box_size, y - half_box_size, box_size, box_size, g );
2960                     }
2961                 }
2962             }
2963         }
2964     }
2965
2966     final private void paintNodeData( final Graphics2D g,
2967                                       final PhylogenyNode node,
2968                                       final boolean to_graphics_file,
2969                                       final boolean to_pdf,
2970                                       final boolean is_in_found_nodes ) {
2971         if ( isNodeDataInvisible( node ) && !to_graphics_file && !to_pdf ) {
2972             return;
2973         }
2974         if ( getOptions().isShowBranchLengthValues()
2975                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
2976                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
2977                 && ( !node.isRoot() ) && ( node.getDistanceToParent() != PhylogenyDataUtil.BRANCH_LENGTH_DEFAULT ) ) {
2978             paintBranchLength( g, node, to_pdf, to_graphics_file );
2979         }
2980         if ( !getControlPanel().isShowInternalData() && !node.isExternal() && !node.isCollapse() ) {
2981             return;
2982         }
2983         int x = 0;
2984         final int half_box_size = getOptions().getDefaultNodeShapeSize() / 2;
2985         if ( getControlPanel().isShowTaxonomyImages()
2986                 && ( getImageMap() != null )
2987                 && !getImageMap().isEmpty()
2988                 && node.getNodeData().isHasTaxonomy()
2989                 && ( ( node.getNodeData().getTaxonomy().getUris() != null ) && !node.getNodeData().getTaxonomy()
2990                         .getUris().isEmpty() ) ) {
2991             x += drawTaxonomyImage( node.getXcoord() + 2 + half_box_size, node.getYcoord(), node, g );
2992         }
2993         if ( ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
2994                 .isShowTaxonomyCommonNames() ) && node.getNodeData().isHasTaxonomy() ) {
2995             x += paintTaxonomy( g, node, is_in_found_nodes, to_pdf, to_graphics_file, x );
2996         }
2997         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
2998             g.setColor( Color.BLACK );
2999         }
3000         else if ( is_in_found_nodes ) {
3001             g.setColor( getTreeColorSet().getFoundColor() );
3002         }
3003         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3004             g.setColor( getTaxonomyBasedColor( node ) );
3005         }
3006         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3007                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3008             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3009         }
3010         else if ( to_pdf ) {
3011             g.setColor( Color.BLACK );
3012         }
3013         else {
3014             g.setColor( getTreeColorSet().getSequenceColor() );
3015         }
3016         _sb.setLength( 0 );
3017         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3018             _sb.append( " [" );
3019             _sb.append( node.getAllExternalDescendants().size() );
3020             _sb.append( "]" );
3021         }
3022         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3023             if ( _sb.length() > 0 ) {
3024                 _sb.append( " " );
3025             }
3026             _sb.append( node.getName() );
3027         }
3028         if ( node.getNodeData().isHasSequence() ) {
3029             if ( getControlPanel().isShowGeneSymbols() && ( node.getNodeData().getSequence().getSymbol().length() > 0 ) ) {
3030                 if ( _sb.length() > 0 ) {
3031                     _sb.append( " " );
3032                 }
3033                 _sb.append( node.getNodeData().getSequence().getSymbol() );
3034             }
3035             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3036                 if ( _sb.length() > 0 ) {
3037                     _sb.append( " " );
3038                 }
3039                 _sb.append( node.getNodeData().getSequence().getName() );
3040             }
3041             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3042                 if ( _sb.length() > 0 ) {
3043                     _sb.append( " " );
3044                 }
3045                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3046                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3047                     _sb.append( ":" );
3048                 }
3049                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3050             }
3051         }
3052         if ( getControlPanel().isShowProperties() && node.getNodeData().isHasProperties() ) {
3053             if ( _sb.length() > 0 ) {
3054                 _sb.append( " " );
3055             }
3056             _sb.append( propertiesToString( node ) );
3057         }
3058         g.setFont( getTreeFontSet().getLargeFont() );
3059         if ( is_in_found_nodes ) {
3060             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3061         }
3062         double down_shift_factor = 3.0;
3063         if ( !node.isExternal() && ( node.getNumberOfDescendants() == 1 ) ) {
3064             down_shift_factor = 1;
3065         }
3066         final double pos_x = node.getXcoord() + x + 2 + half_box_size;
3067         final double pos_y = ( node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ) );
3068         final String sb_str = _sb.toString();
3069         // GUILHEM_BEG ______________
3070         if ( _control_panel.isShowSequenceRelations() && node.getNodeData().isHasSequence()
3071                 && ( _query_sequence != null ) ) {
3072             int nodeTextBoundsWidth = 0;
3073             if ( sb_str.length() > 0 ) {
3074                 final Rectangle2D node_text_bounds = new TextLayout( sb_str, g.getFont(), _frc ).getBounds(); //would like to remove this 'new', but how...
3075                 nodeTextBoundsWidth = ( int ) node_text_bounds.getWidth();
3076             }
3077             if ( node.getNodeData().getSequence().equals( _query_sequence ) ) {
3078                 if ( nodeTextBoundsWidth > 0 ) { // invert font color and background color to show that this is the query sequence
3079                     g.fillRect( ( int ) pos_x - 1, ( int ) pos_y - 8, nodeTextBoundsWidth + 5, 11 );
3080                     g.setColor( getTreeColorSet().getBackgroundColor() );
3081                 }
3082             }
3083             else {
3084                 final List<SequenceRelation> seqRelations = node.getNodeData().getSequence().getSequenceRelations();
3085                 for( final SequenceRelation seqRelation : seqRelations ) {
3086                     final boolean fGotRelationWithQuery = ( seqRelation.getRef0().isEqual( _query_sequence ) || seqRelation
3087                             .getRef1().isEqual( _query_sequence ) )
3088                             && seqRelation.getType().equals( getControlPanel().getSequenceRelationTypeBox()
3089                                     .getSelectedItem() );
3090                     if ( fGotRelationWithQuery ) { // we will underline the text to show that this sequence is ortholog to the query
3091                         final double linePosX = node.getXcoord() + 2 + half_box_size;
3092                         final String sConfidence = ( !getControlPanel().isShowSequenceRelationConfidence() || ( seqRelation
3093                                 .getConfidence() == null ) ) ? null : " (" + seqRelation.getConfidence().getValue()
3094                                 + ")";
3095                         if ( sConfidence != null ) {
3096                             double confidenceX = pos_x;
3097                             if ( sb_str.length() > 0 ) {
3098                                 confidenceX += new TextLayout( sb_str, g.getFont(), _frc ).getBounds().getWidth()
3099                                         + CONFIDENCE_LEFT_MARGIN;
3100                             }
3101                             if ( confidenceX > linePosX ) { // let's only display confidence value if we are already displaying at least one of Prot/Gene Name and Taxonomy Code 
3102                                 final int confidenceWidth = ( int ) new TextLayout( sConfidence, g.getFont(), _frc )
3103                                         .getBounds().getWidth();
3104                                 TreePanel.drawString( sConfidence, confidenceX, pos_y, g );
3105                                 x += CONFIDENCE_LEFT_MARGIN + confidenceWidth;
3106                             }
3107                         }
3108                         if ( x + nodeTextBoundsWidth > 0 ) /* we only underline if there is something displayed */
3109                         {
3110                             if ( nodeTextBoundsWidth == 0 ) {
3111                                 nodeTextBoundsWidth -= 3; /* the gap between taxonomy code and node name should not be underlined if nothing comes after it */
3112                             }
3113                             else {
3114                                 nodeTextBoundsWidth += 2;
3115                             }
3116                             g.drawLine( ( int ) linePosX + 1, 3 + ( int ) pos_y, ( int ) linePosX + x
3117                                     + nodeTextBoundsWidth, 3 + ( int ) pos_y );
3118                             break;
3119                         }
3120                     }
3121                 }
3122             }
3123         }
3124         if ( sb_str.length() > 0 ) {
3125             TreePanel.drawString( sb_str, pos_x, pos_y, g );
3126         }
3127         // GUILHEM_END _____________
3128         // COMMENTED_OUT_BY_GUILHEM_BEG _______________
3129         // TODO FIXME need to check this one!
3130         //if ( _sb.length() > 0 ) {
3131         //    TreePanel.drawString( _sb.toString(), node.getXcoord() + x + 2 + TreePanel.HALF_BOX_SIZE, node.getYcoord()
3132         //            + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3133         //}
3134         // COMMENTED_OUT_BY_GUILHEM_END ________________
3135         if ( getControlPanel().isShowAnnotation() && node.getNodeData().isHasSequence()
3136                 && ( node.getNodeData().getSequence().getAnnotations() != null )
3137                 && ( !node.getNodeData().getSequence().getAnnotations().isEmpty() ) ) {
3138             if ( _sb.length() > 0 ) {
3139                 x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3140             }
3141             final Annotation ann = node.getNodeData().getSequence().getAnnotation( 0 );
3142             if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3143                 g.setColor( Color.BLACK );
3144             }
3145             else {
3146                 g.setColor( calculateColorForAnnotation( ann ) );
3147             }
3148             final String ann_str = ann.asSimpleText().toString();
3149             TreePanel.drawString( ann_str, node.getXcoord() + x + 3 + half_box_size, node.getYcoord()
3150                     + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3151             _sb.setLength( 0 );
3152             _sb.append( ann_str );
3153         }
3154         if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
3155                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE )
3156                 || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED ) ) {
3157             if ( ( getControlPanel().isShowBinaryCharacters() || getControlPanel().isShowBinaryCharacterCounts() )
3158                     && node.getNodeData().isHasBinaryCharacters() ) {
3159                 if ( _sb.length() > 0 ) {
3160                     x += getTreeFontSet()._fm_large.stringWidth( _sb.toString() ) + 5;
3161                 }
3162                 if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3163                     g.setColor( Color.BLACK );
3164                 }
3165                 else {
3166                     g.setColor( getTreeColorSet().getBinaryDomainCombinationsColor() );
3167                 }
3168                 if ( getControlPanel().isShowBinaryCharacters() ) {
3169                     TreePanel.drawString( node.getNodeData().getBinaryCharacters().getPresentCharactersAsStringBuffer()
3170                             .toString(), node.getXcoord() + x + 1 + half_box_size, node.getYcoord()
3171                             + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3172                     paintGainedAndLostCharacters( g, node, node.getNodeData().getBinaryCharacters()
3173                             .getGainedCharactersAsStringBuffer().toString(), node.getNodeData().getBinaryCharacters()
3174                             .getLostCharactersAsStringBuffer().toString() );
3175                 }
3176                 else {
3177                     if ( DRAW_MEAN_COUNTS && node.isInternal() ) {
3178                         final List<PhylogenyNode> ec = node.getAllExternalDescendants();
3179                         double sum = 0;
3180                         int count = 0;
3181                         for( final PhylogenyNode phylogenyNode : ec ) {
3182                             count++;
3183                             if ( phylogenyNode.getNodeData().getBinaryCharacters() != null ) {
3184                                 sum += phylogenyNode.getNodeData().getBinaryCharacters().getPresentCount();
3185                             }
3186                         }
3187                         final double mean = ForesterUtil.round( sum / count, 1 );
3188                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount() + " ["
3189                                 + mean + "]", node.getXcoord() + x + 4 + half_box_size, node.getYcoord()
3190                                 + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ), g );
3191                     }
3192                     else {
3193                         TreePanel.drawString( " " + node.getNodeData().getBinaryCharacters().getPresentCount(),
3194                                               node.getXcoord() + x + 4 + half_box_size,
3195                                               node.getYcoord()
3196                                                       + ( getTreeFontSet()._fm_large.getAscent() / down_shift_factor ),
3197                                               g );
3198                     }
3199                     paintGainedAndLostCharacters( g, node, "+"
3200                             + node.getNodeData().getBinaryCharacters().getGainedCount(), "-"
3201                             + node.getNodeData().getBinaryCharacters().getLostCount() );
3202                 }
3203             }
3204         }
3205     }
3206
3207     private StringBuffer propertiesToString( final PhylogenyNode node ) {
3208         final PropertiesMap properties = node.getNodeData().getProperties();
3209         final StringBuffer sb = new StringBuffer();
3210         boolean first = true;
3211         for( final String ref : properties.getPropertyRefs() ) {
3212             if ( first ) {
3213                 first = false;
3214             }
3215             else {
3216                 sb.append( " " );
3217             }
3218             final Property p = properties.getProperty( ref );
3219             sb.append( getPartAfterColon( p.getRef() ) );
3220             sb.append( "=" );
3221             sb.append( p.getValue() );
3222             if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
3223                 sb.append( getPartAfterColon( p.getUnit() ) );
3224             }
3225         }
3226         return sb;
3227     }
3228
3229     final private static String getPartAfterColon( final String s ) {
3230         final int i = s.indexOf( ':' );
3231         if ( ( i < 1 ) || ( i == ( s.length() - 1 ) ) ) {
3232             return s;
3233         }
3234         return s.substring( i + 1, s.length() );
3235     }
3236
3237     private double drawTaxonomyImage( final double x, final double y, final PhylogenyNode node, final Graphics2D g ) {
3238         final List<Uri> us = new ArrayList<Uri>();
3239         for( final Taxonomy t : node.getNodeData().getTaxonomies() ) {
3240             for( final Uri uri : t.getUris() ) {
3241                 us.add( uri );
3242             }
3243         }
3244         double offset = 0;
3245         for( final Uri uri : us ) {
3246             if ( uri != null ) {
3247                 final String uri_str = uri.getValue().toString().toLowerCase();
3248                 if ( getImageMap().containsKey( uri_str ) ) {
3249                     final BufferedImage bi = getImageMap().get( uri_str );
3250                     if ( ( bi != null ) && ( bi.getHeight() > 5 ) && ( bi.getWidth() > 5 ) ) {
3251                         double scaling_factor = 1;
3252                         if ( getOptions().isAllowMagnificationOfTaxonomyImages()
3253                                 || ( bi.getHeight() > ( 1.8 * getYdistance() ) ) ) {
3254                             scaling_factor = ( 1.8 * getYdistance() ) / bi.getHeight();
3255                         }
3256                         // y = y - ( 0.9 * getYdistance() );
3257                         final double hs = bi.getHeight() * scaling_factor;
3258                         double ws = bi.getWidth() * scaling_factor + offset;
3259                         final double my_y = y - ( 0.5 * hs );
3260                         final int x_w = ( int ) ( x + ws + 0.5 );
3261                         final int y_h = ( int ) ( my_y + hs + 0.5 );
3262                         if ( ( x_w - x > 7 ) && ( y_h - my_y > 7 ) ) {
3263                             g.drawImage( bi,
3264                                          ( int ) ( x + 0.5 + offset ),
3265                                          ( int ) ( my_y + 0.5 ),
3266                                          x_w,
3267                                          y_h,
3268                                          0,
3269                                          0,
3270                                          bi.getWidth(),
3271                                          bi.getHeight(),
3272                                          null );
3273                             ws += 8;
3274                         }
3275                         else {
3276                             ws = 0.0;
3277                         }
3278                         offset = ws;
3279                     }
3280                 }
3281             }
3282         }
3283         return offset;
3284     }
3285
3286     final private void paintNodeDataUnrootedCirc( final Graphics2D g,
3287                                                   final PhylogenyNode node,
3288                                                   final boolean to_pdf,
3289                                                   final boolean to_graphics_file,
3290                                                   final boolean radial_labels,
3291                                                   final double ur_angle,
3292                                                   final boolean is_in_found_nodes ) {
3293         if ( isNodeDataInvisibleUnrootedCirc( node ) && !to_graphics_file && !to_pdf ) {
3294             return;
3295         }
3296         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3297             g.setColor( Color.BLACK );
3298         }
3299         else if ( is_in_found_nodes ) {
3300             g.setColor( getTreeColorSet().getFoundColor() );
3301         }
3302         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3303             g.setColor( getTaxonomyBasedColor( node ) );
3304         }
3305         else {
3306             g.setColor( getTreeColorSet().getSequenceColor() );
3307         }
3308         _sb.setLength( 0 );
3309         _sb.append( " " );
3310         if ( node.getNodeData().isHasTaxonomy()
3311                 && ( getControlPanel().isShowTaxonomyCode() || getControlPanel().isShowTaxonomyScientificNames() || getControlPanel()
3312                         .isShowTaxonomyCommonNames() ) ) {
3313             final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3314             if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3315                 _sb.append( taxonomy.getTaxonomyCode() );
3316                 _sb.append( " " );
3317             }
3318             if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3319                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3320                         && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3321                     _sb.append( taxonomy.getScientificName() );
3322                     _sb.append( " (" );
3323                     _sb.append( taxonomy.getCommonName() );
3324                     _sb.append( ") " );
3325                 }
3326                 else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3327                     _sb.append( taxonomy.getScientificName() );
3328                     _sb.append( " " );
3329                 }
3330                 else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3331                     _sb.append( taxonomy.getCommonName() );
3332                     _sb.append( " " );
3333                 }
3334             }
3335             else if ( _control_panel.isShowTaxonomyScientificNames() ) {
3336                 if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3337                     _sb.append( taxonomy.getScientificName() );
3338                     _sb.append( " " );
3339                 }
3340             }
3341             else if ( _control_panel.isShowTaxonomyCommonNames() ) {
3342                 if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3343                     _sb.append( taxonomy.getCommonName() );
3344                     _sb.append( " " );
3345                 }
3346             }
3347         }
3348         if ( node.isCollapse() && ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) ) {
3349             _sb.append( " [" );
3350             _sb.append( node.getAllExternalDescendants().size() );
3351             _sb.append( "]" );
3352         }
3353         if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3354             if ( _sb.length() > 0 ) {
3355                 _sb.append( " " );
3356             }
3357             _sb.append( node.getName() );
3358         }
3359         if ( node.getNodeData().isHasSequence() ) {
3360             if ( getControlPanel().isShowSequenceAcc() && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3361                 if ( _sb.length() > 0 ) {
3362                     _sb.append( " " );
3363                 }
3364                 if ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getAccession().getSource() ) ) {
3365                     _sb.append( node.getNodeData().getSequence().getAccession().getSource() );
3366                     _sb.append( ":" );
3367                 }
3368                 _sb.append( node.getNodeData().getSequence().getAccession().getValue() );
3369             }
3370             if ( getControlPanel().isShowGeneNames() && ( node.getNodeData().getSequence().getName().length() > 0 ) ) {
3371                 if ( _sb.length() > 0 ) {
3372                     _sb.append( " " );
3373                 }
3374                 _sb.append( node.getNodeData().getSequence().getName() );
3375             }
3376         }
3377         g.setFont( getTreeFontSet().getLargeFont() );
3378         if ( is_in_found_nodes ) {
3379             g.setFont( getTreeFontSet().getLargeFont().deriveFont( Font.BOLD ) );
3380         }
3381         if ( _sb.length() > 1 ) {
3382             final String sb_str = _sb.toString();
3383             double m = 0;
3384             if ( _graphics_type == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3385                 m = _urt_nodeid_angle_map.get( node.getId() ) % TWO_PI;
3386             }
3387             else {
3388                 m = ( float ) ( ur_angle % TWO_PI );
3389             }
3390             _at = g.getTransform();
3391             boolean need_to_reset = false;
3392             final float x_coord = node.getXcoord();
3393             final float y_coord = node.getYcoord() + ( getTreeFontSet()._fm_large.getAscent() / 3.0f );
3394             if ( radial_labels ) {
3395                 need_to_reset = true;
3396                 boolean left = false;
3397                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3398                     m -= PI;
3399                     left = true;
3400                 }
3401                 g.rotate( m, x_coord, node.getYcoord() );
3402                 if ( left ) {
3403                     g.translate( -( getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth() ), 0 );
3404                 }
3405             }
3406             else {
3407                 if ( ( m > HALF_PI ) && ( m < ONEHALF_PI ) ) {
3408                     need_to_reset = true;
3409                     g.translate( -getTreeFontSet()._fm_large.getStringBounds( sb_str, g ).getWidth(), 0 );
3410                 }
3411             }
3412             TreePanel.drawString( sb_str, x_coord, y_coord, g );
3413             if ( need_to_reset ) {
3414                 g.setTransform( _at );
3415             }
3416         }
3417     }
3418
3419     final private void paintNodeLite( final Graphics2D g, final PhylogenyNode node ) {
3420         if ( node.isCollapse() ) {
3421             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3422                 paintCollapsedNode( g, node, false, false, false );
3423             }
3424             return;
3425         }
3426         if ( isInFoundNodes( node ) ) {
3427             g.setColor( getTreeColorSet().getFoundColor() );
3428             drawRectFilled( node.getXSecondary() - 1, node.getYSecondary() - 1, 3, 3, g );
3429         }
3430         float new_x = 0;
3431         if ( !node.isExternal() && !node.isCollapse() ) {
3432             boolean first_child = true;
3433             float y2 = 0.0f;
3434             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3435             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3436                 final PhylogenyNode child_node = node.getChildNode( i );
3437                 int factor_x;
3438                 if ( !isUniformBranchLengthsForCladogram() ) {
3439                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3440                 }
3441                 else {
3442                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3443                 }
3444                 if ( first_child ) {
3445                     first_child = false;
3446                     y2 = node.getYSecondary()
3447                             - ( getOvYDistance() * ( node.getNumberOfExternalNodes() - child_node
3448                                     .getNumberOfExternalNodes() ) );
3449                 }
3450                 else {
3451                     y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3452                 }
3453                 final float x2 = calculateOvBranchLengthToParent( child_node, factor_x );
3454                 new_x = x2 + node.getXSecondary();
3455                 final float diff_y = node.getYSecondary() - y2;
3456                 final float diff_x = node.getXSecondary() - new_x;
3457                 if ( ( diff_y > 2 ) || ( diff_y < -2 ) || ( diff_x > 2 ) || ( diff_x < -2 ) ) {
3458                     paintBranchLite( g, node.getXSecondary(), new_x, node.getYSecondary(), y2, child_node );
3459                 }
3460                 child_node.setXSecondary( new_x );
3461                 child_node.setYSecondary( y2 );
3462                 y2 += getOvYDistance() * child_node.getNumberOfExternalNodes();
3463             }
3464         }
3465     }
3466
3467     final private void paintNodeRectangular( final Graphics2D g,
3468                                              final PhylogenyNode node,
3469                                              final boolean to_pdf,
3470                                              final boolean dynamically_hide,
3471                                              final int dynamic_hiding_factor,
3472                                              final boolean to_graphics_file ) {
3473         final boolean is_in_found_nodes = isInFoundNodes( node );
3474         if ( node.isCollapse() ) {
3475             if ( ( !node.isRoot() && !node.getParent().isCollapse() ) || node.isRoot() ) {
3476                 paintCollapsedNode( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3477             }
3478             return;
3479         }
3480         if ( node.isExternal() ) {
3481             ++_external_node_index;
3482         }
3483         // Confidence values
3484         if ( getControlPanel().isShowConfidenceValues()
3485                 && !node.isExternal()
3486                 && !node.isRoot()
3487                 && ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.ROUNDED )
3488                         || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR ) || ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE ) )
3489                 && node.getBranchData().isHasConfidences() ) {
3490             paintConfidenceValues( g, node, to_pdf, to_graphics_file );
3491         }
3492         // Draw a line to root:
3493         if ( node.isRoot() && _phylogeny.isRooted() ) {
3494             paintRootBranch( g, node.getXcoord(), node.getYcoord(), node, to_pdf, to_graphics_file );
3495         }
3496         float new_x = 0;
3497         float new_x_min = Float.MAX_VALUE;
3498         final boolean disallow_shortcutting = dynamic_hiding_factor < 40;
3499         float min_dist = 1.5f;
3500         if ( !disallow_shortcutting ) {
3501             //   System.out.println( dynamic_hiding_factor );
3502             if ( dynamic_hiding_factor > 4000 ) {
3503                 min_dist = 4;
3504             }
3505             else if ( dynamic_hiding_factor > 1000 ) {
3506                 min_dist = 3;
3507             }
3508             else if ( dynamic_hiding_factor > 100 ) {
3509                 min_dist = 2;
3510             }
3511         }
3512         if ( !node.isExternal() && !node.isCollapse() ) {
3513             boolean first_child = true;
3514             float y2 = 0.0f;
3515             final int parent_max_branch_to_leaf = getMaxBranchesToLeaf( node );
3516             for( int i = 0; i < node.getNumberOfDescendants(); ++i ) {
3517                 final PhylogenyNode child_node = node.getChildNode( i );
3518                 int factor_x;
3519                 if ( !isUniformBranchLengthsForCladogram() ) {
3520                     factor_x = node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes();
3521                 }
3522                 else {
3523                     factor_x = parent_max_branch_to_leaf - getMaxBranchesToLeaf( child_node );
3524                 }
3525                 if ( first_child ) {
3526                     first_child = false;
3527                     y2 = node.getYcoord()
3528                             - ( _y_distance * ( node.getNumberOfExternalNodes() - child_node.getNumberOfExternalNodes() ) );
3529                 }
3530                 else {
3531                     y2 += _y_distance * child_node.getNumberOfExternalNodes();
3532                 }
3533                 final float x2 = calculateBranchLengthToParent( child_node, factor_x );
3534                 new_x = x2 + node.getXcoord();
3535                 if ( dynamically_hide && ( x2 < new_x_min ) ) {
3536                     new_x_min = x2;
3537                 }
3538                 final float diff_y = node.getYcoord() - y2;
3539                 final float diff_x = node.getXcoord() - new_x;
3540                 if ( disallow_shortcutting || ( diff_y > min_dist ) || ( diff_y < -min_dist ) || ( diff_x > min_dist )
3541                         || ( diff_x < -min_dist ) || to_graphics_file || to_pdf ) {
3542                     paintBranchRectangular( g,
3543                                             node.getXcoord(),
3544                                             new_x,
3545                                             node.getYcoord(),
3546                                             y2,
3547                                             child_node,
3548                                             to_pdf,
3549                                             to_graphics_file );
3550                 }
3551                 child_node.setXcoord( new_x );
3552                 child_node.setYcoord( y2 );
3553                 y2 += _y_distance * child_node.getNumberOfExternalNodes();
3554             }
3555             paintNodeBox( node.getXcoord(), node.getYcoord(), node, g, to_pdf, to_graphics_file, isInFoundNodes( node ) );
3556         }
3557         if ( dynamically_hide
3558                 && !is_in_found_nodes
3559                 && ( ( node.isExternal() && ( _external_node_index % dynamic_hiding_factor != 1 ) ) || ( !node
3560                         .isExternal() && ( ( new_x_min < 20 ) || ( _y_distance * node.getNumberOfExternalNodes() < getTreeFontSet()._fm_large
3561                         .getHeight() ) ) ) ) ) {
3562             return;
3563         }
3564         paintNodeData( g, node, to_graphics_file, to_pdf, is_in_found_nodes );
3565         paintNodeWithRenderableData( g, node, to_graphics_file, to_pdf );
3566     }
3567
3568     final private void paintNodeWithRenderableData( final Graphics2D g,
3569                                                     final PhylogenyNode node,
3570                                                     final boolean to_graphics_file,
3571                                                     final boolean to_pdf ) {
3572         if ( isNodeDataInvisible( node ) && !to_graphics_file ) {
3573             return;
3574         }
3575         if ( ( !getControlPanel().isShowInternalData() && !node.isExternal() ) ) {
3576             return;
3577         }
3578         if ( getControlPanel().isShowDomainArchitectures() && node.getNodeData().isHasSequence()
3579                 && ( node.getNodeData().getSequence().getDomainArchitecture() != null ) ) {
3580             RenderableDomainArchitecture rds = null;
3581             if ( node.getNodeData().getSequence().getDomainArchitecture() instanceof RenderableDomainArchitecture ) {
3582                 try {
3583                     rds = ( RenderableDomainArchitecture ) node.getNodeData().getSequence().getDomainArchitecture();
3584                 }
3585                 catch ( final ClassCastException cce ) {
3586                     cce.printStackTrace();
3587                 }
3588                 if ( rds != null ) {
3589                     rds.setRenderingHeight( 6 );
3590                     int x = 0;
3591                     if ( getControlPanel().isShowTaxonomyCode()
3592                             && ( !ForesterUtil.isEmpty( PhylogenyMethods.getSpecies( node ) ) ) ) {
3593                         x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( node ) + " " );
3594                     }
3595                     if ( getControlPanel().isShowGeneNames()
3596                             && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getName() ) ) ) {
3597                         x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getName() + " " );
3598                     }
3599                     if ( getControlPanel().isShowGeneSymbols()
3600                             && ( !ForesterUtil.isEmpty( node.getNodeData().getSequence().getSymbol() ) ) ) {
3601                         x += getTreeFontSet()._fm_large
3602                                 .stringWidth( node.getNodeData().getSequence().getSymbol() + " " );
3603                     }
3604                     if ( getControlPanel().isShowSequenceAcc()
3605                             && ( node.getNodeData().getSequence().getAccession() != null ) ) {
3606                         x += getTreeFontSet()._fm_large.stringWidth( node.getNodeData().getSequence().getAccession()
3607                                 .toString()
3608                                 + " " );
3609                     }
3610                     if ( getControlPanel().isShowNodeNames() && ( node.getName().length() > 0 ) ) {
3611                         x += getTreeFontSet()._fm_large.stringWidth( node.getName() + " " );
3612                     }
3613                     rds.render( node.getXcoord() + x, node.getYcoord() - 3, g, this, to_pdf );
3614                 }
3615             }
3616         }
3617         //////////////
3618         if ( getControlPanel().isShowVectorData() && ( node.getNodeData().getVector() != null )
3619                 && ( node.getNodeData().getVector().size() > 0 ) && ( getStatisticsForExpressionValues() != null ) ) {
3620             final RenderableVector rv = RenderableVector.createInstance( node.getNodeData().getVector(),
3621                                                                          getStatisticsForExpressionValues(),
3622                                                                          getConfiguration() );
3623             if ( rv != null ) {
3624                 int x = 0;
3625                 PhylogenyNode my_node = node;
3626                 if ( !getControlPanel().isDrawPhylogram() ) {
3627                     my_node = getPhylogeny().getFirstExternalNode();
3628                 }
3629                 if ( getControlPanel().isShowTaxonomyCode() && ( PhylogenyMethods.getSpecies( my_node ).length() > 0 ) ) {
3630                     x += getTreeFontSet()._fm_large_italic.stringWidth( PhylogenyMethods.getSpecies( my_node ) + " " );
3631                 }
3632                 if ( getControlPanel().isShowNodeNames() && ( my_node.getName().length() > 0 ) ) {
3633                     x += getTreeFontSet()._fm_large.stringWidth( my_node.getName() + " " );
3634                 }
3635                 rv.render( my_node.getXcoord() + x, node.getYcoord() - 5, g, this, to_pdf );
3636             }
3637         }
3638         //////////////
3639     }
3640
3641     final private void paintOvRectangle( final Graphics2D g ) {
3642         final float w_ratio = ( float ) getWidth() / getVisibleRect().width;
3643         final float h_ratio = ( float ) getHeight() / getVisibleRect().height;
3644         final float x_ratio = ( float ) getWidth() / getVisibleRect().x;
3645         final float y_ratio = ( float ) getHeight() / getVisibleRect().y;
3646         final float width = getOvMaxWidth() / w_ratio;
3647         final float height = getOvMaxHeight() / h_ratio;
3648         final float x = getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / x_ratio;
3649         final float y = getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / y_ratio;
3650         g.setColor( getTreeColorSet().getFoundColor() );
3651         getOvRectangle().setRect( x, y, width, height );
3652         if ( ( width < 6 ) && ( height < 6 ) ) {
3653             drawRectFilled( x, y, 6, 6, g );
3654             getOvVirtualRectangle().setRect( x, y, 6, 6 );
3655         }
3656         else if ( width < 6 ) {
3657             drawRectFilled( x, y, 6, height, g );
3658             getOvVirtualRectangle().setRect( x, y, 6, height );
3659         }
3660         else if ( height < 6 ) {
3661             drawRectFilled( x, y, width, 6, g );
3662             getOvVirtualRectangle().setRect( x, y, width, 6 );
3663         }
3664         else {
3665             drawRect( x, y, width, height, g );
3666             if ( isInOvRect() ) {
3667                 drawRect( x + 1, y + 1, width - 2, height - 2, g );
3668             }
3669             getOvVirtualRectangle().setRect( x, y, width, height );
3670         }
3671     }
3672
3673     final void paintPhylogeny( final Graphics2D g,
3674                                final boolean to_pdf,
3675                                final boolean to_graphics_file,
3676                                final int graphics_file_width,
3677                                final int graphics_file_height,
3678                                final int graphics_file_x,
3679                                final int graphics_file_y ) {
3680         if ( ( _phylogeny == null ) || _phylogeny.isEmpty() ) {
3681             return;
3682         }
3683         if ( _control_panel.isShowSequenceRelations() ) {
3684             _query_sequence = _control_panel.getSelectedQuerySequence();
3685         }
3686         // Color the background
3687         if ( !to_pdf ) {
3688             final Rectangle r = getVisibleRect();
3689             if ( !getOptions().isBackgroundColorGradient() || getOptions().isPrintBlackAndWhite() ) {
3690                 g.setColor( getTreeColorSet().getBackgroundColor() );
3691                 if ( !to_graphics_file ) {
3692                     g.fill( r );
3693                 }
3694                 else {
3695                     if ( getOptions().isPrintBlackAndWhite() ) {
3696                         g.setColor( Color.WHITE );
3697                     }
3698                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3699                 }
3700             }
3701             else {
3702                 if ( !to_graphics_file ) {
3703                     g.setPaint( new GradientPaint( r.x, r.y, getTreeColorSet().getBackgroundColor(), r.x, r.y
3704                             + r.height, getTreeColorSet().getBackgroundColorGradientBottom() ) );
3705                     g.fill( r );
3706                 }
3707                 else {
3708                     g.setPaint( new GradientPaint( graphics_file_x,
3709                                                    graphics_file_y,
3710                                                    getTreeColorSet().getBackgroundColor(),
3711                                                    graphics_file_x,
3712                                                    graphics_file_y + graphics_file_height,
3713                                                    getTreeColorSet().getBackgroundColorGradientBottom() ) );
3714                     g.fillRect( graphics_file_x, graphics_file_y, graphics_file_width, graphics_file_height );
3715                 }
3716             }
3717             g.setStroke( new BasicStroke( 1 ) );
3718         }
3719         else {
3720             g.setStroke( new BasicStroke( getOptions().getPrintLineWidth() ) );
3721         }
3722         if ( ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.UNROOTED )
3723                 && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
3724             _external_node_index = 0;
3725             // Position starting X of tree
3726             if ( !_phylogeny.isRooted() /*|| ( _subtree_index > 0 )*/) {
3727                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE );
3728             }
3729             else if ( ( _phylogeny.getRoot().getDistanceToParent() > 0.0 ) && getControlPanel().isDrawPhylogram() ) {
3730                 _phylogeny.getRoot().setXcoord( ( float ) ( TreePanel.MOVE + ( _phylogeny.getRoot()
3731                         .getDistanceToParent() * getXcorrectionFactor() ) ) );
3732             }
3733             else {
3734                 _phylogeny.getRoot().setXcoord( TreePanel.MOVE + getXdistance() );
3735             }
3736             // Position starting Y of tree
3737             _phylogeny.getRoot().setYcoord( ( getYdistance() * _phylogeny.getRoot().getNumberOfExternalNodes() )
3738                     + ( TreePanel.MOVE / 2.0f ) );
3739             final int dynamic_hiding_factor = ( int ) ( getTreeFontSet()._fm_large.getHeight() / ( 1.5 * getYdistance() ) );
3740             if ( getControlPanel().isDynamicallyHideData() ) {
3741                 if ( dynamic_hiding_factor > 1 ) {
3742                     getControlPanel().setDynamicHidingIsOn( true );
3743                 }
3744                 else {
3745                     getControlPanel().setDynamicHidingIsOn( false );
3746                 }
3747             }
3748             if ( _nodes_in_preorder == null ) {
3749                 _nodes_in_preorder = new PhylogenyNode[ _phylogeny.getNodeCount() ];
3750                 int i = 0;
3751                 for( final PhylogenyNodeIterator it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3752                     _nodes_in_preorder[ i++ ] = it.next();
3753                 }
3754             }
3755             //final PhylogenyNodeIterator it;
3756             //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3757             //    paintNodeRectangular( g, it.next(), to_pdf, getControlPanel().isDynamicallyHideData()
3758             //            && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3759             //}
3760             for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3761                 paintNodeRectangular( g, _nodes_in_preorder[ i ], to_pdf, getControlPanel().isDynamicallyHideData()
3762                         && ( dynamic_hiding_factor > 1 ), dynamic_hiding_factor, to_graphics_file );
3763             }
3764             if ( getOptions().isShowScale() && getControlPanel().isDrawPhylogram() && ( getScaleDistance() > 0.0 ) ) {
3765                 if ( !( to_graphics_file || to_pdf ) ) {
3766                     paintScale( g,
3767                                 getVisibleRect().x,
3768                                 getVisibleRect().y + getVisibleRect().height,
3769                                 to_pdf,
3770                                 to_graphics_file );
3771                 }
3772                 else {
3773                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3774                 }
3775             }
3776             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3777                 paintPhylogenyLite( g );
3778             }
3779         }
3780         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
3781             if ( getControlPanel().getDynamicallyHideData() != null ) {
3782                 getControlPanel().setDynamicHidingIsOn( false );
3783             }
3784             final double angle = getStartingAngle();
3785             final boolean radial_labels = getOptions().getNodeLabelDirection() == NODE_LABEL_DIRECTION.RADIAL;
3786             _dynamic_hiding_factor = 0;
3787             if ( getControlPanel().isDynamicallyHideData() ) {
3788                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3789                         .getNumberOfExternalNodes() ) / ( TWO_PI * 10 ) );
3790             }
3791             if ( getControlPanel().getDynamicallyHideData() != null ) {
3792                 if ( _dynamic_hiding_factor > 1 ) {
3793                     getControlPanel().setDynamicHidingIsOn( true );
3794                 }
3795                 else {
3796                     getControlPanel().setDynamicHidingIsOn( false );
3797                 }
3798             }
3799             paintUnrooted( _phylogeny.getRoot(),
3800                            angle,
3801                            ( float ) ( angle + 2 * Math.PI ),
3802                            radial_labels,
3803                            g,
3804                            to_pdf,
3805                            to_graphics_file );
3806             if ( getOptions().isShowScale() ) {
3807                 if ( !( to_graphics_file || to_pdf ) ) {
3808                     paintScale( g,
3809                                 getVisibleRect().x,
3810                                 getVisibleRect().y + getVisibleRect().height,
3811                                 to_pdf,
3812                                 to_graphics_file );
3813                 }
3814                 else {
3815                     paintScale( g, graphics_file_x, graphics_file_y + graphics_file_height, to_pdf, to_graphics_file );
3816                 }
3817             }
3818             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3819                 g.setColor( getTreeColorSet().getOvColor() );
3820                 paintUnrootedLite( _phylogeny.getRoot(),
3821                                    angle,
3822                                    angle + 2 * Math.PI,
3823                                    g,
3824                                    ( getUrtFactorOv() / ( getVisibleRect().width / getOvMaxWidth() ) ) );
3825                 paintOvRectangle( g );
3826             }
3827         }
3828         else if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
3829             final int radius = ( int ) ( ( Math.min( getPreferredSize().getWidth(), getPreferredSize().getHeight() ) / 2 ) - ( MOVE + getLongestExtNodeInfo() ) );
3830             final int d = radius + MOVE + getLongestExtNodeInfo();
3831             _dynamic_hiding_factor = 0;
3832             if ( getControlPanel().isDynamicallyHideData() && ( radius > 0 ) ) {
3833                 _dynamic_hiding_factor = ( int ) ( ( getTreeFontSet()._fm_large.getHeight() * 1.5 * getPhylogeny()
3834                         .getNumberOfExternalNodes() ) / ( TWO_PI * radius ) );
3835             }
3836             if ( getControlPanel().getDynamicallyHideData() != null ) {
3837                 if ( _dynamic_hiding_factor > 1 ) {
3838                     getControlPanel().setDynamicHidingIsOn( true );
3839                 }
3840                 else {
3841                     getControlPanel().setDynamicHidingIsOn( false );
3842                 }
3843             }
3844             paintCircular( _phylogeny, getStartingAngle(), d, d, radius > 0 ? radius : 0, g, to_pdf, to_graphics_file );
3845             if ( getOptions().isShowOverview() && isOvOn() && !to_graphics_file && !to_pdf ) {
3846                 final int radius_ov = ( int ) ( getOvMaxHeight() < getOvMaxWidth() ? getOvMaxHeight() / 2
3847                         : getOvMaxWidth() / 2 );
3848                 double x_scale = 1.0;
3849                 double y_scale = 1.0;
3850                 int x_pos = getVisibleRect().x + getOvXPosition();
3851                 int y_pos = getVisibleRect().y + getOvYPosition();
3852                 if ( getWidth() > getHeight() ) {
3853                     x_scale = ( double ) getHeight() / getWidth();
3854                     x_pos = ForesterUtil.roundToInt( x_pos / x_scale );
3855                 }
3856                 else {
3857                     y_scale = ( double ) getWidth() / getHeight();
3858                     y_pos = ForesterUtil.roundToInt( y_pos / y_scale );
3859                 }
3860                 _at = g.getTransform();
3861                 g.scale( x_scale, y_scale );
3862                 paintCircularLite( _phylogeny,
3863                                    getStartingAngle(),
3864                                    x_pos + radius_ov,
3865                                    y_pos + radius_ov,
3866                                    ( int ) ( radius_ov - ( getLongestExtNodeInfo() / ( getVisibleRect().width / getOvRectangle()
3867                                            .getWidth() ) ) ),
3868                                    g );
3869                 g.setTransform( _at );
3870                 paintOvRectangle( g );
3871             }
3872         }
3873     }
3874
3875     final private void paintPhylogenyLite( final Graphics2D g ) {
3876         //System.out.println( getVisibleRect().x + " " + getVisibleRect().y );
3877         _phylogeny
3878                 .getRoot()
3879                 .setXSecondary( ( float ) ( getVisibleRect().x + getOvXPosition() + ( MOVE / ( getVisibleRect().width / getOvRectangle()
3880                         .getWidth() ) ) ) );
3881         _phylogeny.getRoot().setYSecondary( ( getVisibleRect().y + getOvYStart() ) );
3882         //final PhylogenyNodeIterator it;
3883         //for( it = _phylogeny.iteratorPreorder(); it.hasNext(); ) {
3884         //    paintNodeLite( g, it.next() );
3885         //}
3886         for( int i = 0; i < _nodes_in_preorder.length; ++i ) {
3887             paintNodeLite( g, _nodes_in_preorder[ i ] );
3888         }
3889         paintOvRectangle( g );
3890     }
3891
3892     /**
3893      * Paint the root branch. (Differs from others because it will always be a
3894      * single horizontal line).
3895      * @param to_graphics_file 
3896      * 
3897      * @return new x1 value
3898      */
3899     final private void paintRootBranch( final Graphics2D g,
3900                                         final float x1,
3901                                         final float y1,
3902                                         final PhylogenyNode root,
3903                                         final boolean to_pdf,
3904                                         final boolean to_graphics_file ) {
3905         assignGraphicsForBranchWithColorForParentBranch( root, false, g, to_pdf, to_graphics_file );
3906         float d = getXdistance();
3907         if ( getControlPanel().isDrawPhylogram() && ( root.getDistanceToParent() > 0.0 ) ) {
3908             d = ( float ) ( getXcorrectionFactor() * root.getDistanceToParent() );
3909         }
3910         if ( d < MIN_ROOT_LENGTH ) {
3911             d = MIN_ROOT_LENGTH;
3912         }
3913         if ( !getControlPanel().isWidthBranches() || ( PhylogenyMethods.getBranchWidthValue( root ) == 1 ) ) {
3914             drawLine( x1 - d, root.getYcoord(), x1, root.getYcoord(), g );
3915         }
3916         else {
3917             final double w = PhylogenyMethods.getBranchWidthValue( root );
3918             drawRectFilled( x1 - d, root.getYcoord() - ( w / 2 ), d, w, g );
3919         }
3920         paintNodeBox( x1, root.getYcoord(), root, g, to_pdf, to_graphics_file, isInFoundNodes( root ) );
3921     }
3922
3923     final private void paintScale( final Graphics2D g,
3924                                    int x1,
3925                                    int y1,
3926                                    final boolean to_pdf,
3927                                    final boolean to_graphics_file ) {
3928         x1 += MOVE;
3929         final double x2 = x1 + ( getScaleDistance() * getXcorrectionFactor() );
3930         y1 -= 12;
3931         final int y2 = y1 - 8;
3932         final int y3 = y1 - 4;
3933         g.setFont( getTreeFontSet().getSmallFont() );
3934         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3935             g.setColor( Color.BLACK );
3936         }
3937         else {
3938             g.setColor( getTreeColorSet().getBranchLengthColor() );
3939         }
3940         drawLine( x1, y1, x1, y2, g );
3941         drawLine( x2, y1, x2, y2, g );
3942         drawLine( x1, y3, x2, y3, g );
3943         if ( getScaleLabel() != null ) {
3944             g.drawString( getScaleLabel(), ( x1 + 2 ), y3 - 2 );
3945         }
3946     }
3947
3948     final private int paintTaxonomy( final Graphics2D g,
3949                                      final PhylogenyNode node,
3950                                      final boolean is_in_found_nodes,
3951                                      final boolean to_pdf,
3952                                      final boolean to_graphics_file,
3953                                      final double x_shift ) {
3954         final Taxonomy taxonomy = node.getNodeData().getTaxonomy();
3955         g.setFont( getTreeFontSet().getLargeItalicFont() );
3956         if ( ( to_pdf || to_graphics_file ) && getOptions().isPrintBlackAndWhite() ) {
3957             g.setColor( Color.BLACK );
3958         }
3959         else if ( is_in_found_nodes ) {
3960             g.setFont( getTreeFontSet().getLargeItalicFont().deriveFont( TreeFontSet.BOLD_AND_ITALIC ) );
3961             g.setColor( getTreeColorSet().getFoundColor() );
3962         }
3963         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
3964             g.setColor( getTaxonomyBasedColor( node ) );
3965         }
3966         else if ( getOptions().isColorLabelsSameAsParentBranch() && getControlPanel().isColorBranches()
3967                 && ( PhylogenyMethods.getBranchColorValue( node ) != null ) ) {
3968             g.setColor( PhylogenyMethods.getBranchColorValue( node ) );
3969         }
3970         else if ( to_pdf ) {
3971             g.setColor( Color.BLACK );
3972         }
3973         else {
3974             g.setColor( getTreeColorSet().getTaxonomyColor() );
3975         }
3976         final double start_x = node.getXcoord() + 3 + ( getOptions().getDefaultNodeShapeSize() / 2 ) + x_shift;
3977         final double start_y = node.getYcoord()
3978                 + ( getTreeFontSet()._fm_large.getAscent() / ( node.getNumberOfDescendants() == 1 ? 1 : 3.0 ) );
3979         _sb.setLength( 0 );
3980         if ( _control_panel.isShowTaxonomyCode() && !ForesterUtil.isEmpty( taxonomy.getTaxonomyCode() ) ) {
3981             _sb.append( taxonomy.getTaxonomyCode() );
3982             _sb.append( " " );
3983         }
3984         if ( _control_panel.isShowTaxonomyScientificNames() && _control_panel.isShowTaxonomyCommonNames() ) {
3985             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() )
3986                     && !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
3987                 if ( getOptions().isAbbreviateScientificTaxonNames()
3988                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
3989                     abbreviateScientificName( taxonomy.getScientificName() );
3990                 }
3991                 else {
3992                     _sb.append( taxonomy.getScientificName() );
3993                 }
3994                 _sb.append( " (" );
3995                 _sb.append( taxonomy.getCommonName() );
3996                 _sb.append( ") " );
3997             }
3998             else if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
3999                 if ( getOptions().isAbbreviateScientificTaxonNames()
4000                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4001                     abbreviateScientificName( taxonomy.getScientificName() );
4002                 }
4003                 else {
4004                     _sb.append( taxonomy.getScientificName() );
4005                 }
4006                 _sb.append( " " );
4007             }
4008             else if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4009                 _sb.append( taxonomy.getCommonName() );
4010                 _sb.append( " " );
4011             }
4012         }
4013         else if ( _control_panel.isShowTaxonomyScientificNames() ) {
4014             if ( !ForesterUtil.isEmpty( taxonomy.getScientificName() ) ) {
4015                 if ( getOptions().isAbbreviateScientificTaxonNames()
4016                         && ( taxonomy.getScientificName().indexOf( ' ' ) > 0 ) ) {
4017                     abbreviateScientificName( taxonomy.getScientificName() );
4018                 }
4019                 else {
4020                     _sb.append( taxonomy.getScientificName() );
4021                 }
4022                 _sb.append( " " );
4023             }
4024         }
4025         else if ( _control_panel.isShowTaxonomyCommonNames() ) {
4026             if ( !ForesterUtil.isEmpty( taxonomy.getCommonName() ) ) {
4027                 _sb.append( taxonomy.getCommonName() );
4028                 _sb.append( " " );
4029             }
4030         }
4031         final String label = _sb.toString();
4032         /* GUILHEM_BEG */
4033         if ( _control_panel.isShowSequenceRelations() && ( label.length() > 0 )
4034                 && ( node.getNodeData().isHasSequence() ) && node.getNodeData().getSequence().equals( _query_sequence ) ) {
4035             // invert font color and background color to show that this is the query sequence
4036             final Rectangle2D nodeTextBounds = new TextLayout( label, g.getFont(), new FontRenderContext( null,
4037                                                                                                           false,
4038                                                                                                           false ) )
4039                     .getBounds();
4040             g.fillRect( ( int ) start_x - 1, ( int ) start_y - 8, ( int ) nodeTextBounds.getWidth() + 4, 11 );
4041             g.setColor( getTreeColorSet().getBackgroundColor() );
4042         }
4043         /* GUILHEM_END */
4044         TreePanel.drawString( label, start_x, start_y, g );
4045         if ( is_in_found_nodes ) {
4046             return getTreeFontSet()._fm_large_italic_bold.stringWidth( label );
4047         }
4048         else {
4049             return getTreeFontSet()._fm_large_italic.stringWidth( label );
4050         }
4051     }
4052
4053     private void abbreviateScientificName( final String sn ) {
4054         final String[] a = sn.split( "\\s+" );
4055         _sb.append( a[ 0 ].substring( 0, 1 ) );
4056         _sb.append( a[ 1 ].substring( 0, 2 ) );
4057         if ( a.length > 2 ) {
4058             for( int i = 2; i < a.length; i++ ) {
4059                 _sb.append( " " );
4060                 _sb.append( a[ i ] );
4061             }
4062         }
4063     }
4064
4065     final private void paintUnrooted( final PhylogenyNode n,
4066                                       final double low_angle,
4067                                       final double high_angle,
4068                                       final boolean radial_labels,
4069                                       final Graphics2D g,
4070                                       final boolean to_pdf,
4071                                       final boolean to_graphics_file ) {
4072         if ( n.isRoot() ) {
4073             n.setXcoord( getWidth() / 2 );
4074             n.setYcoord( getHeight() / 2 );
4075         }
4076         if ( n.isExternal() ) {
4077             paintNodeDataUnrootedCirc( g,
4078                                        n,
4079                                        to_pdf,
4080                                        to_graphics_file,
4081                                        radial_labels,
4082                                        ( high_angle + low_angle ) / 2,
4083                                        isInFoundNodes( n ) );
4084             return;
4085         }
4086         final float num_enclosed = n.getNumberOfExternalNodes();
4087         final float x = n.getXcoord();
4088         final float y = n.getYcoord();
4089         double current_angle = low_angle;
4090         // final boolean n_below = n.getYcoord() < getVisibleRect().getMinY() - 20;
4091         // final boolean n_above = n.getYcoord() > getVisibleRect().getMaxY() + 20;
4092         // final boolean n_left = n.getXcoord() < getVisibleRect().getMinX() - 20;
4093         // final boolean n_right = n.getXcoord() > getVisibleRect().getMaxX() + 20;
4094         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4095             final PhylogenyNode desc = n.getChildNode( i );
4096             ///  if ( ( ( n_below ) & ( desc.getYcoord() < getVisibleRect().getMinY() - 20 ) )
4097             //          || ( ( n_above ) & ( desc.getYcoord() > getVisibleRect().getMaxY() + 20 ) )
4098             //         || ( ( n_left ) & ( desc.getXcoord() < getVisibleRect().getMinX() - 20 ) )
4099             //          || ( ( n_right ) & ( desc.getXcoord() > getVisibleRect().getMaxX() + 20 ) ) ) {
4100             //     continue;
4101             // }
4102             //if ( ( desc.getYcoord() > n.getYcoord() ) && ( n.getYcoord() > getVisibleRect().getMaxY() - 20 ) ) {
4103             //    continue;
4104             //}
4105             //if ( ( desc.getYcoord() < n.getYcoord() ) && ( n.getYcoord() < getVisibleRect().getMinY() + 20 ) ) {
4106             //    continue;
4107             // }
4108             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4109             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4110             float length;
4111             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4112                 if ( desc.getDistanceToParent() < 0 ) {
4113                     length = 0;
4114                 }
4115                 else {
4116                     length = ( float ) ( desc.getDistanceToParent() * getUrtFactor() );
4117                 }
4118             }
4119             else {
4120                 length = getUrtFactor();
4121             }
4122             final double mid_angle = current_angle + arc_size / 2;
4123             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4124             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4125             desc.setXcoord( new_x );
4126             desc.setYcoord( new_y );
4127             paintUnrooted( desc, current_angle, current_angle + arc_size, radial_labels, g, to_pdf, to_graphics_file );
4128             current_angle += arc_size;
4129             assignGraphicsForBranchWithColorForParentBranch( desc, false, g, to_pdf, to_graphics_file );
4130             drawLine( x, y, new_x, new_y, g );
4131             paintNodeBox( new_x, new_y, desc, g, to_pdf, to_graphics_file, isInFoundNodes( desc ) );
4132         }
4133         if ( n.isRoot() ) {
4134             paintNodeBox( n.getXcoord(), n.getYcoord(), n, g, to_pdf, to_graphics_file, isInFoundNodes( n ) );
4135         }
4136     }
4137
4138     final private void paintUnrootedLite( final PhylogenyNode n,
4139                                           final double low_angle,
4140                                           final double high_angle,
4141                                           final Graphics2D g,
4142                                           final float urt_ov_factor ) {
4143         if ( n.isRoot() ) {
4144             final int x_pos = ( int ) ( getVisibleRect().x + getOvXPosition() + getOvMaxWidth() / 2 );
4145             final int y_pos = ( int ) ( getVisibleRect().y + getOvYPosition() + getOvMaxHeight() / 2 );
4146             n.setXSecondary( x_pos );
4147             n.setYSecondary( y_pos );
4148         }
4149         if ( n.isExternal() ) {
4150             return;
4151         }
4152         final float num_enclosed = n.getNumberOfExternalNodes();
4153         final float x = n.getXSecondary();
4154         final float y = n.getYSecondary();
4155         double current_angle = low_angle;
4156         for( int i = 0; i < n.getNumberOfDescendants(); ++i ) {
4157             final PhylogenyNode desc = n.getChildNode( i );
4158             final int desc_num_enclosed = desc.getNumberOfExternalNodes();
4159             final double arc_size = ( desc_num_enclosed / num_enclosed ) * ( high_angle - low_angle );
4160             float length;
4161             if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4162                 if ( desc.getDistanceToParent() < 0 ) {
4163                     length = 0;
4164                 }
4165                 else {
4166                     length = ( float ) ( desc.getDistanceToParent() * urt_ov_factor );
4167                 }
4168             }
4169             else {
4170                 length = urt_ov_factor;
4171             }
4172             final double mid_angle = current_angle + arc_size / 2;
4173             final float new_x = ( float ) ( x + Math.cos( mid_angle ) * length );
4174             final float new_y = ( float ) ( y + Math.sin( mid_angle ) * length );
4175             desc.setXSecondary( new_x );
4176             desc.setYSecondary( new_y );
4177             if ( isInFoundNodes( desc ) ) {
4178                 g.setColor( getTreeColorSet().getFoundColor() );
4179                 drawRectFilled( desc.getXSecondary() - 1, desc.getYSecondary() - 1, 3, 3, g );
4180                 g.setColor( getTreeColorSet().getOvColor() );
4181             }
4182             paintUnrootedLite( desc, current_angle, current_angle + arc_size, g, urt_ov_factor );
4183             current_angle += arc_size;
4184             drawLine( x, y, new_x, new_y, g );
4185         }
4186     }
4187
4188     final private void pasteSubtree( final PhylogenyNode node ) {
4189         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4190             errorMessageNoCutCopyPasteInUnrootedDisplay();
4191             return;
4192         }
4193         if ( ( getCutOrCopiedTree() == null ) || getCutOrCopiedTree().isEmpty() ) {
4194             JOptionPane.showMessageDialog( this,
4195                                            "No tree in buffer (need to copy or cut a subtree first)",
4196                                            "Attempt to paste with empty buffer",
4197                                            JOptionPane.ERROR_MESSAGE );
4198             return;
4199         }
4200         final String label = getASimpleTextRepresentationOfANode( getCutOrCopiedTree().getRoot() );
4201         final Object[] options = { "As sibling", "As descendant", "Cancel" };
4202         final int r = JOptionPane.showOptionDialog( this,
4203                                                     "How to paste subtree" + label + "?",
4204                                                     "Paste Subtree",
4205                                                     JOptionPane.CLOSED_OPTION,
4206                                                     JOptionPane.QUESTION_MESSAGE,
4207                                                     null,
4208                                                     options,
4209                                                     options[ 2 ] );
4210         boolean paste_as_sibling = true;
4211         if ( r == 1 ) {
4212             paste_as_sibling = false;
4213         }
4214         else if ( r != 0 ) {
4215             return;
4216         }
4217         final Phylogeny buffer_phy = getCutOrCopiedTree().copy();
4218         buffer_phy.setAllNodesToNotCollapse();
4219         buffer_phy.preOrderReId();
4220         buffer_phy.setRooted( true );
4221         boolean need_to_show_whole = false;
4222         if ( paste_as_sibling ) {
4223             if ( node.isRoot() ) {
4224                 JOptionPane.showMessageDialog( this,
4225                                                "Cannot paste sibling to root",
4226                                                "Attempt to paste sibling to root",
4227                                                JOptionPane.ERROR_MESSAGE );
4228                 return;
4229             }
4230             buffer_phy.addAsSibling( node );
4231         }
4232         else {
4233             if ( ( node.getNumberOfExternalNodes() == 1 ) && node.isRoot() ) {
4234                 need_to_show_whole = true;
4235                 _phylogeny = buffer_phy;
4236             }
4237             else {
4238                 buffer_phy.addAsChild( node );
4239             }
4240         }
4241         if ( getCopiedAndPastedNodes() == null ) {
4242             setCopiedAndPastedNodes( new HashSet<Integer>() );
4243         }
4244         final List<PhylogenyNode> nodes = PhylogenyMethods.obtainAllNodesAsList( buffer_phy );
4245         final Set<Integer> node_ids = new HashSet<Integer>( nodes.size() );
4246         for( final PhylogenyNode n : nodes ) {
4247             node_ids.add( n.getId() );
4248         }
4249         node_ids.add( node.getId() );
4250         getCopiedAndPastedNodes().addAll( node_ids );
4251         setNodeInPreorderToNull();
4252         _phylogeny.externalNodesHaveChanged();
4253         _phylogeny.clearHashIdToNodeMap();
4254         _phylogeny.recalculateNumberOfExternalDescendants( true );
4255         resetNodeIdToDistToLeafMap();
4256         setEdited( true );
4257         if ( need_to_show_whole ) {
4258             getControlPanel().showWhole();
4259         }
4260         repaint();
4261     }
4262
4263     final public int print( final Graphics g, final PageFormat page_format, final int page_index )
4264             throws PrinterException {
4265         if ( page_index > 0 ) {
4266             return ( NO_SUCH_PAGE );
4267         }
4268         else {
4269             final Graphics2D g2d = ( Graphics2D ) g;
4270             g2d.translate( page_format.getImageableX(), page_format.getImageableY() );
4271             // Turn off double buffering !?
4272             paintPhylogeny( g2d, true, false, 0, 0, 0, 0 );
4273             // Turn double buffering back on !?
4274             return ( PAGE_EXISTS );
4275         }
4276     }
4277
4278     final void recalculateMaxDistanceToRoot() {
4279         _max_distance_to_root = PhylogenyMethods.calculateMaxDistanceToRoot( getPhylogeny() );
4280     }
4281
4282     /**
4283      * Remove all edit-node frames
4284      */
4285     final void removeAllEditNodeJFrames() {
4286         for( int i = 0; i <= ( TreePanel.MAX_NODE_FRAMES - 1 ); i++ ) {
4287             if ( _node_frames[ i ] != null ) {
4288                 _node_frames[ i ].dispose();
4289                 _node_frames[ i ] = null;
4290             }
4291         }
4292         _node_frame_index = 0;
4293     }
4294
4295     /**
4296      * Remove a node-edit frame.
4297      */
4298     final void removeEditNodeFrame( final int i ) {
4299         _node_frame_index--;
4300         _node_frames[ i ] = null;
4301         if ( i < _node_frame_index ) {
4302             for( int j = 0; j < _node_frame_index - 1; j++ ) {
4303                 _node_frames[ j ] = _node_frames[ j + 1 ];
4304             }
4305             _node_frames[ _node_frame_index ] = null;
4306         }
4307     }
4308
4309     final void reRoot( final PhylogenyNode node ) {
4310         if ( !getPhylogeny().isRerootable() ) {
4311             JOptionPane.showMessageDialog( this,
4312                                            "This is not rerootable",
4313                                            "Not rerootable",
4314                                            JOptionPane.WARNING_MESSAGE );
4315             return;
4316         }
4317         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4318             JOptionPane.showMessageDialog( this,
4319                                            "Cannot reroot in unrooted display type",
4320                                            "Attempt to reroot tree in unrooted display",
4321                                            JOptionPane.WARNING_MESSAGE );
4322             return;
4323         }
4324         getPhylogeny().reRoot( node );
4325         getPhylogeny().recalculateNumberOfExternalDescendants( true );
4326         resetNodeIdToDistToLeafMap();
4327         setNodeInPreorderToNull();
4328         resetPreferredSize();
4329         getMainPanel().adjustJScrollPane();
4330         repaint();
4331         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) {
4332             getControlPanel().showWhole();
4333         }
4334     }
4335
4336     final void resetNodeIdToDistToLeafMap() {
4337         _nodeid_dist_to_leaf = new HashMap<Integer, Short>();
4338     }
4339
4340     final void resetPreferredSize() {
4341         if ( ( getPhylogeny() == null ) || getPhylogeny().isEmpty() ) {
4342             return;
4343         }
4344         int x = 0;
4345         int y = 0;
4346         y = TreePanel.MOVE
4347                 + ForesterUtil.roundToInt( getYdistance() * getPhylogeny().getRoot().getNumberOfExternalNodes() * 2 );
4348         if ( getControlPanel().isDrawPhylogram() ) {
4349             x = TreePanel.MOVE
4350                     + getLongestExtNodeInfo()
4351                     + ForesterUtil
4352                             .roundToInt( ( getXcorrectionFactor() * getPhylogeny().getHeight() ) + getXdistance() );
4353         }
4354         else {
4355             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4356                 x = TreePanel.MOVE
4357                         + getLongestExtNodeInfo()
4358                         + ForesterUtil.roundToInt( getXdistance()
4359                                 * ( getPhylogeny().getRoot().getNumberOfExternalNodes() + 2 ) );
4360             }
4361             else {
4362                 x = TreePanel.MOVE
4363                         + getLongestExtNodeInfo()
4364                         + ForesterUtil.roundToInt( getXdistance()
4365                                 * ( PhylogenyMethods.calculateMaxDepth( getPhylogeny() ) + 1 ) );
4366             }
4367         }
4368         setPreferredSize( new Dimension( x, y ) );
4369     }
4370
4371     public final void setArrowCursor() {
4372         setCursor( ARROW_CURSOR );
4373         repaint();
4374     }
4375
4376     final void setControlPanel( final ControlPanel atv_control ) {
4377         _control_panel = atv_control;
4378     }
4379
4380     final private void setCopiedAndPastedNodes( final Set<Integer> nodeIds ) {
4381         getMainPanel().setCopiedAndPastedNodes( nodeIds );
4382     }
4383
4384     final private void setCutOrCopiedTree( final Phylogeny cut_or_copied_tree ) {
4385         getMainPanel().setCutOrCopiedTree( cut_or_copied_tree );
4386     }
4387
4388     public final void setEdited( final boolean edited ) {
4389         _edited = edited;
4390     }
4391
4392     final void setFoundNodes( final Set<Integer> found_nodes ) {
4393         _found_nodes = found_nodes;
4394     }
4395
4396     final private void setInOv( final boolean in_ov ) {
4397         _in_ov = in_ov;
4398     }
4399
4400     final void setInOvRect( final boolean in_ov_rect ) {
4401         _in_ov_rect = in_ov_rect;
4402     }
4403
4404     final void setLargeFonts() {
4405         getTreeFontSet().largeFonts();
4406     }
4407
4408     final void setLastMouseDragPointX( final float x ) {
4409         _last_drag_point_x = x;
4410     }
4411
4412     final void setLastMouseDragPointY( final float y ) {
4413         _last_drag_point_y = y;
4414     }
4415
4416     final void setLongestExtNodeInfo( final int i ) {
4417         _longest_ext_node_info = i;
4418     }
4419
4420     final void setMediumFonts() {
4421         getTreeFontSet().mediumFonts();
4422     }
4423
4424     final private void setOvMaxHeight( final float ov_max_height ) {
4425         _ov_max_height = ov_max_height;
4426     }
4427
4428     final private void setOvMaxWidth( final float ov_max_width ) {
4429         _ov_max_width = ov_max_width;
4430     }
4431
4432     final void setOvOn( final boolean ov_on ) {
4433         _ov_on = ov_on;
4434     }
4435
4436     final private void setOvXcorrectionFactor( final float f ) {
4437         _ov_x_correction_factor = f;
4438     }
4439
4440     final private void setOvXDistance( final float ov_x_distance ) {
4441         _ov_x_distance = ov_x_distance;
4442     }
4443
4444     final private void setOvXPosition( final int ov_x_position ) {
4445         _ov_x_position = ov_x_position;
4446     }
4447
4448     final private void setOvYDistance( final float ov_y_distance ) {
4449         _ov_y_distance = ov_y_distance;
4450     }
4451
4452     final private void setOvYPosition( final int ov_y_position ) {
4453         _ov_y_position = ov_y_position;
4454     }
4455
4456     final private void setOvYStart( final int ov_y_start ) {
4457         _ov_y_start = ov_y_start;
4458     }
4459
4460     /**
4461      * Set parameters for printing the displayed tree
4462      * 
4463      * @param x
4464      * @param y
4465      */
4466     public final void setParametersForPainting( final int x, final int y, final boolean recalc_longest_ext_node_info ) {
4467         // updateStyle(); not needed?
4468         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4469             initNodeData();
4470             if ( recalc_longest_ext_node_info ) {
4471                 calculateLongestExtNodeInfo();
4472             }
4473             int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
4474             final int max_depth = PhylogenyMethods.calculateMaxDepth( _phylogeny );
4475             if ( ext_nodes == 1 ) {
4476                 ext_nodes = max_depth;
4477                 if ( ext_nodes < 1 ) {
4478                     ext_nodes = 1;
4479                 }
4480             }
4481             updateOvSizes();
4482             float xdist = 0;
4483             float ov_xdist = 0;
4484             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
4485                 xdist = ( float ) ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( ext_nodes + 3.0 ) );
4486                 ov_xdist = ( float ) ( getOvMaxWidth() / ( ext_nodes + 3.0 ) );
4487             }
4488             else {
4489                 xdist = ( ( x - getLongestExtNodeInfo() - TreePanel.MOVE ) / ( max_depth + 1 ) );
4490                 ov_xdist = ( getOvMaxWidth() / ( max_depth + 1 ) );
4491             }
4492             float ydist = ( float ) ( ( y - TreePanel.MOVE ) / ( ext_nodes * 2.0 ) );
4493             if ( xdist < 0.0 ) {
4494                 xdist = 0.0f;
4495             }
4496             if ( ov_xdist < 0.0 ) {
4497                 ov_xdist = 0.0f;
4498             }
4499             if ( ydist < 0.0 ) {
4500                 ydist = 0.0f;
4501             }
4502             setXdistance( xdist );
4503             setYdistance( ydist );
4504             setOvXDistance( ov_xdist );
4505             final double height = _phylogeny.getHeight();
4506             if ( height > 0 ) {
4507                 final float corr = ( float ) ( ( x - TreePanel.MOVE - getLongestExtNodeInfo() - getXdistance() ) / height );
4508                 setXcorrectionFactor( corr > 0 ? corr : 0 );
4509                 final float ov_corr = ( float ) ( ( getOvMaxWidth() - getOvXDistance() ) / height );
4510                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
4511             }
4512             else {
4513                 setXcorrectionFactor( 0 );
4514                 setOvXcorrectionFactor( 0 );
4515             }
4516             _circ_max_depth = max_depth;
4517             setUpUrtFactor();
4518         }
4519     }
4520
4521     final void setPhylogenyGraphicsType( final PHYLOGENY_GRAPHICS_TYPE graphics_type ) {
4522         _graphics_type = graphics_type;
4523         setTextAntialias();
4524     }
4525
4526     final private void setScaleDistance( final double scale_distance ) {
4527         _scale_distance = scale_distance;
4528     }
4529
4530     final private void setScaleLabel( final String scale_label ) {
4531         _scale_label = scale_label;
4532     }
4533
4534     final void setSmallFonts() {
4535         getTreeFontSet().smallFonts();
4536     }
4537
4538     final void setStartingAngle( final double starting_angle ) {
4539         _urt_starting_angle = starting_angle;
4540     }
4541
4542     final void setSuperTinyFonts() {
4543         getTreeFontSet().superTinyFonts();
4544     }
4545
4546     final void setTextAntialias() {
4547         if ( ( _phylogeny != null ) && !_phylogeny.isEmpty() ) {
4548             if ( _phylogeny.getNumberOfExternalNodes() <= LIMIT_FOR_HQ_RENDERING ) {
4549                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
4550             }
4551             else {
4552                 _rendering_hints.put( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
4553             }
4554         }
4555         if ( getMainPanel().getOptions().isAntialiasScreen() ) {
4556             if ( ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR )
4557                     && !getMainPanel().getOptions().isShowDefaultNodeShapes()
4558                     && ( ( getControlPanel() != null ) && !getControlPanel().isShowDomainArchitectures() ) ) {
4559                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4560             }
4561             else {
4562                 _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
4563             }
4564             try {
4565                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING,
4566                                       RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB );
4567             }
4568             catch ( final Throwable e ) {
4569                 _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
4570             }
4571         }
4572         else {
4573             _rendering_hints.put( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF );
4574             _rendering_hints.put( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
4575         }
4576     }
4577
4578     final void setTinyFonts() {
4579         getTreeFontSet().tinyFonts();
4580     }
4581
4582     /**
4583      * Set a phylogeny tree.
4584      * 
4585      * @param t
4586      *            an instance of a Phylogeny
4587      */
4588     public final void setTree( final Phylogeny t ) {
4589         setNodeInPreorderToNull();
4590         _phylogeny = t;
4591     }
4592
4593     final void setNodeInPreorderToNull() {
4594         _nodes_in_preorder = null;
4595     }
4596
4597     final void setTreeFile( final File treefile ) {
4598         _treefile = treefile;
4599     }
4600
4601     final private void setUpUrtFactor() {
4602         final int d = getVisibleRect().width < getVisibleRect().height ? getVisibleRect().width
4603                 : getVisibleRect().height;
4604         if ( isPhyHasBranchLengths() && getControlPanel().isDrawPhylogram() ) {
4605             setUrtFactor( ( float ) ( d / ( 2 * getMaxDistanceToRoot() ) ) );
4606         }
4607         else {
4608             final int max_depth = _circ_max_depth;
4609             if ( max_depth > 0 ) {
4610                 setUrtFactor( d / ( 2 * max_depth ) );
4611             }
4612             else {
4613                 setUrtFactor( d / 2 );
4614             }
4615         }
4616         setUrtFactorOv( getUrtFactor() );
4617     }
4618
4619     final private void setUrtFactor( final float urt_factor ) {
4620         _urt_factor = urt_factor;
4621     }
4622
4623     final private void setUrtFactorOv( final float urt_factor_ov ) {
4624         _urt_factor_ov = urt_factor_ov;
4625     }
4626
4627     public final void setWaitCursor() {
4628         setCursor( WAIT_CURSOR );
4629         repaint();
4630     }
4631
4632     final void setXcorrectionFactor( final float f ) {
4633         _x_correction_factor = f;
4634     }
4635
4636     final void setXdistance( final float x ) {
4637         _x_distance = x;
4638     }
4639
4640     final void setYdistance( final float y ) {
4641         _y_distance = y;
4642     }
4643
4644     final private void showNodeDataPopup( final MouseEvent e, final PhylogenyNode node ) {
4645         try {
4646             if ( ( node.getName().length() > 0 )
4647                     || ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) )
4648                     || ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) )
4649                     || ( node.getNodeData().isHasDate() ) || ( node.getNodeData().isHasDistribution() )
4650                     || node.getBranchData().isHasConfidences() ) {
4651                 _popup_buffer.setLength( 0 );
4652                 short lines = 0;
4653                 if ( node.getName().length() > 0 ) {
4654                     lines++;
4655                     _popup_buffer.append( node.getName() );
4656                 }
4657                 if ( node.getNodeData().isHasTaxonomy() && !isTaxonomyEmpty( node.getNodeData().getTaxonomy() ) ) {
4658                     lines++;
4659                     boolean enc_data = false;
4660                     final Taxonomy tax = node.getNodeData().getTaxonomy();
4661                     if ( _popup_buffer.length() > 0 ) {
4662                         _popup_buffer.append( "\n" );
4663                     }
4664                     if ( !ForesterUtil.isEmpty( tax.getTaxonomyCode() ) ) {
4665                         _popup_buffer.append( "[" );
4666                         _popup_buffer.append( tax.getTaxonomyCode() );
4667                         _popup_buffer.append( "]" );
4668                         enc_data = true;
4669                     }
4670                     if ( !ForesterUtil.isEmpty( tax.getScientificName() ) ) {
4671                         if ( enc_data ) {
4672                             _popup_buffer.append( " " );
4673                         }
4674                         _popup_buffer.append( tax.getScientificName() );
4675                         enc_data = true;
4676                     }
4677                     if ( !ForesterUtil.isEmpty( tax.getCommonName() ) ) {
4678                         if ( enc_data ) {
4679                             _popup_buffer.append( " (" );
4680                         }
4681                         else {
4682                             _popup_buffer.append( "(" );
4683                         }
4684                         _popup_buffer.append( tax.getCommonName() );
4685                         _popup_buffer.append( ")" );
4686                         enc_data = true;
4687                     }
4688                     if ( !ForesterUtil.isEmpty( tax.getAuthority() ) ) {
4689                         if ( enc_data ) {
4690                             _popup_buffer.append( " (" );
4691                         }
4692                         else {
4693                             _popup_buffer.append( "(" );
4694                         }
4695                         _popup_buffer.append( tax.getAuthority() );
4696                         _popup_buffer.append( ")" );
4697                         enc_data = true;
4698                     }
4699                     if ( !ForesterUtil.isEmpty( tax.getRank() ) ) {
4700                         if ( enc_data ) {
4701                             _popup_buffer.append( " [" );
4702                         }
4703                         else {
4704                             _popup_buffer.append( "[" );
4705                         }
4706                         _popup_buffer.append( tax.getRank() );
4707                         _popup_buffer.append( "]" );
4708                         enc_data = true;
4709                     }
4710                     if ( tax.getSynonyms().size() > 0 ) {
4711                         if ( enc_data ) {
4712                             _popup_buffer.append( " " );
4713                         }
4714                         _popup_buffer.append( "[" );
4715                         int counter = 1;
4716                         for( final String syn : tax.getSynonyms() ) {
4717                             if ( !ForesterUtil.isEmpty( syn ) ) {
4718                                 enc_data = true;
4719                                 _popup_buffer.append( syn );
4720                                 if ( counter < tax.getSynonyms().size() ) {
4721                                     _popup_buffer.append( ", " );
4722                                 }
4723                             }
4724                             counter++;
4725                         }
4726                         _popup_buffer.append( "]" );
4727                     }
4728                     if ( !enc_data ) {
4729                         if ( ( tax.getIdentifier() != null ) && !ForesterUtil.isEmpty( tax.getIdentifier().getValue() ) ) {
4730                             if ( !ForesterUtil.isEmpty( tax.getIdentifier().getProvider() ) ) {
4731                                 _popup_buffer.append( "[" );
4732                                 _popup_buffer.append( tax.getIdentifier().getProvider() );
4733                                 _popup_buffer.append( "] " );
4734                             }
4735                             _popup_buffer.append( tax.getIdentifier().getValue() );
4736                         }
4737                     }
4738                 }
4739                 if ( node.getNodeData().isHasSequence() && !isSequenceEmpty( node.getNodeData().getSequence() ) ) {
4740                     lines++;
4741                     boolean enc_data = false;
4742                     if ( _popup_buffer.length() > 0 ) {
4743                         _popup_buffer.append( "\n" );
4744                     }
4745                     final Sequence seq = node.getNodeData().getSequence();
4746                     if ( seq.getAccession() != null ) {
4747                         _popup_buffer.append( "[" );
4748                         if ( !ForesterUtil.isEmpty( seq.getAccession().getSource() ) ) {
4749                             _popup_buffer.append( seq.getAccession().getSource() );
4750                             _popup_buffer.append( ":" );
4751                         }
4752                         _popup_buffer.append( seq.getAccession().getValue() );
4753                         _popup_buffer.append( "]" );
4754                         enc_data = true;
4755                     }
4756                     if ( !ForesterUtil.isEmpty( seq.getSymbol() ) ) {
4757                         if ( enc_data ) {
4758                             _popup_buffer.append( " [" );
4759                         }
4760                         else {
4761                             _popup_buffer.append( "[" );
4762                         }
4763                         _popup_buffer.append( seq.getSymbol() );
4764                         _popup_buffer.append( "]" );
4765                         enc_data = true;
4766                     }
4767                     if ( !ForesterUtil.isEmpty( seq.getName() ) ) {
4768                         if ( enc_data ) {
4769                             _popup_buffer.append( " " );
4770                         }
4771                         _popup_buffer.append( seq.getName() );
4772                     }
4773                 }
4774                 if ( node.getNodeData().isHasDate() ) {
4775                     lines++;
4776                     if ( _popup_buffer.length() > 0 ) {
4777                         _popup_buffer.append( "\n" );
4778                     }
4779                     _popup_buffer.append( node.getNodeData().getDate().asSimpleText() );
4780                 }
4781                 if ( node.getNodeData().isHasDistribution() ) {
4782                     lines++;
4783                     if ( _popup_buffer.length() > 0 ) {
4784                         _popup_buffer.append( "\n" );
4785                     }
4786                     _popup_buffer.append( node.getNodeData().getDistribution().asSimpleText() );
4787                 }
4788                 if ( node.getBranchData().isHasConfidences() ) {
4789                     final List<Confidence> confs = node.getBranchData().getConfidences();
4790                     for( final Confidence confidence : confs ) {
4791                         lines++;
4792                         if ( _popup_buffer.length() > 0 ) {
4793                             _popup_buffer.append( "\n" );
4794                         }
4795                         if ( !ForesterUtil.isEmpty( confidence.getType() ) ) {
4796                             _popup_buffer.append( "[" );
4797                             _popup_buffer.append( confidence.getType() );
4798                             _popup_buffer.append( "] " );
4799                         }
4800                         _popup_buffer
4801                                 .append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence.getValue(),
4802                                                                                           getOptions()
4803                                                                                                   .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4804                         if ( confidence.getStandardDeviation() != Confidence.CONFIDENCE_DEFAULT_VALUE ) {
4805                             _popup_buffer.append( " (sd=" );
4806                             _popup_buffer.append( FORMATTER_CONFIDENCE.format( ForesterUtil.round( confidence
4807                                     .getStandardDeviation(), getOptions()
4808                                     .getNumberOfDigitsAfterCommaForConfidenceValues() ) ) );
4809                             _popup_buffer.append( ")" );
4810                         }
4811                     }
4812                 }
4813                 if ( node.getNodeData().isHasProperties() ) {
4814                     final PropertiesMap properties = node.getNodeData().getProperties();
4815                     for( final String ref : properties.getPropertyRefs() ) {
4816                         _popup_buffer.append( "\n" );
4817                         final Property p = properties.getProperty( ref );
4818                         _popup_buffer.append( getPartAfterColon( p.getRef() ) );
4819                         _popup_buffer.append( "=" );
4820                         _popup_buffer.append( p.getValue() );
4821                         if ( !ForesterUtil.isEmpty( p.getUnit() ) ) {
4822                             _popup_buffer.append( getPartAfterColon( p.getUnit() ) );
4823                         }
4824                     }
4825                 }
4826                 if ( _popup_buffer.length() > 0 ) {
4827                     if ( !getConfiguration().isUseNativeUI() ) {
4828                         _rollover_popup
4829                                 .setBorder( BorderFactory.createLineBorder( getTreeColorSet().getBranchColor() ) );
4830                         _rollover_popup.setBackground( getTreeColorSet().getBackgroundColor() );
4831                         if ( isInFoundNodes( node ) ) {
4832                             _rollover_popup.setForeground( getTreeColorSet().getFoundColor() );
4833                         }
4834                         else if ( getControlPanel().isColorAccordingToTaxonomy() ) {
4835                             _rollover_popup.setForeground( getTaxonomyBasedColor( node ) );
4836                         }
4837                         else {
4838                             _rollover_popup.setForeground( getTreeColorSet().getSequenceColor() );
4839                         }
4840                     }
4841                     else {
4842                         _rollover_popup.setBorder( BorderFactory.createLineBorder( Color.BLACK ) );
4843                     }
4844                     _rollover_popup.setText( _popup_buffer.toString() );
4845                     _node_desc_popup = PopupFactory.getSharedInstance().getPopup( null,
4846                                                                                   _rollover_popup,
4847                                                                                   e.getLocationOnScreen().x + 10,
4848                                                                                   e.getLocationOnScreen().y
4849                                                                                           - ( lines * 20 ) );
4850                     _node_desc_popup.show();
4851                 }
4852             }
4853         }
4854         catch ( final Exception ex ) {
4855             // Do nothing.
4856         }
4857     }
4858
4859     final private void showNodeEditFrame( final PhylogenyNode n ) {
4860         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4861             // pop up edit box for single node
4862             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index, "" );
4863             _node_frame_index++;
4864         }
4865         else {
4866             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4867         }
4868     }
4869
4870     final private void showNodeFrame( final PhylogenyNode n ) {
4871         if ( _node_frame_index < TreePanel.MAX_NODE_FRAMES ) {
4872             // pop up edit box for single node
4873             _node_frames[ _node_frame_index ] = new NodeFrame( n, _phylogeny, this, _node_frame_index );
4874             _node_frame_index++;
4875         }
4876         else {
4877             JOptionPane.showMessageDialog( this, "too many node windows are open" );
4878         }
4879     }
4880
4881     final void subTree( final PhylogenyNode node ) {
4882         if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
4883             JOptionPane.showMessageDialog( this,
4884                                            "Cannot get a sub/super tree in unrooted display",
4885                                            "Attempt to get sub/super tree in unrooted display",
4886                                            JOptionPane.WARNING_MESSAGE );
4887             return;
4888         }
4889         if ( node.isExternal() ) {
4890             JOptionPane.showMessageDialog( this,
4891                                            "Cannot get a subtree of a external node",
4892                                            "Attempt to get subtree of external node",
4893                                            JOptionPane.WARNING_MESSAGE );
4894             return;
4895         }
4896         if ( node.isRoot() && !isCurrentTreeIsSubtree() ) {
4897             JOptionPane.showMessageDialog( this,
4898                                            "Cannot get a subtree of the root node",
4899                                            "Attempt to get subtree of root node",
4900                                            JOptionPane.WARNING_MESSAGE );
4901             return;
4902         }
4903         setNodeInPreorderToNull();
4904         if ( !node.isExternal() && !node.isRoot() && ( _subtree_index <= ( TreePanel.MAX_SUBTREES - 1 ) ) ) {
4905             _sub_phylogenies[ _subtree_index ] = _phylogeny;
4906             _sub_phylogenies_temp_roots[ _subtree_index ] = node;
4907             ++_subtree_index;
4908             _phylogeny = subTree( node, _phylogeny );
4909             updateSubSuperTreeButton();
4910         }
4911         else if ( node.isRoot() && isCurrentTreeIsSubtree() ) {
4912             superTree();
4913         }
4914         _main_panel.getControlPanel().showWhole();
4915         repaint();
4916     }
4917
4918     final boolean isCurrentTreeIsSubtree() {
4919         return ( _subtree_index > 0 );
4920     }
4921
4922     final private static Phylogeny subTree( final PhylogenyNode new_root, final Phylogeny source_phy ) {
4923         final Phylogeny new_phy = new Phylogeny();
4924         new_phy.setRooted( true );
4925         new_phy.setName( source_phy.getName() );
4926         new_phy.setDescription( source_phy.getDescription() );
4927         new_phy.setType( source_phy.getType() );
4928         new_phy.setDistanceUnit( source_phy.getDistanceUnit() );
4929         new_phy.setConfidence( source_phy.getConfidence() );
4930         new_phy.setIdentifier( source_phy.getIdentifier() );
4931         new_phy.setRoot( new_root.copyNodeDataShallow() );
4932         int i = 0;
4933         for( final PhylogenyNode n : new_root.getDescendants() ) {
4934             new_phy.getRoot().setChildNode( i++, n );
4935         }
4936         return new_phy;
4937     }
4938
4939     final void superTree() {
4940         setNodeInPreorderToNull();
4941         final PhylogenyNode temp_root = _sub_phylogenies_temp_roots[ _subtree_index - 1 ];
4942         for( final PhylogenyNode n : temp_root.getDescendants() ) {
4943             n.setParent( temp_root );
4944         }
4945         _sub_phylogenies[ _subtree_index ] = null;
4946         _sub_phylogenies_temp_roots[ _subtree_index ] = null;
4947         _phylogeny = _sub_phylogenies[ --_subtree_index ];
4948         updateSubSuperTreeButton();
4949     }
4950
4951     final void swap( final PhylogenyNode node ) {
4952         if ( node.isExternal() || ( node.getNumberOfDescendants() < 2 ) ) {
4953             return;
4954         }
4955         if ( node.getNumberOfDescendants() > 2 ) {
4956             JOptionPane.showMessageDialog( this,
4957                                            "Cannot swap descendants of nodes with more than 2 descendants",
4958                                            "Cannot swap descendants",
4959                                            JOptionPane.ERROR_MESSAGE );
4960             return;
4961         }
4962         if ( !node.isExternal() ) {
4963             node.swapChildren();
4964             setNodeInPreorderToNull();
4965             _phylogeny.externalNodesHaveChanged();
4966             _phylogeny.clearHashIdToNodeMap();
4967             _phylogeny.recalculateNumberOfExternalDescendants( true );
4968             resetNodeIdToDistToLeafMap();
4969             setEdited( true );
4970         }
4971         repaint();
4972     }
4973
4974     final void sortDescendants( final PhylogenyNode node ) {
4975         if ( !node.isExternal() ) {
4976             DESCENDANT_SORT_PRIORITY pri = DESCENDANT_SORT_PRIORITY.TAXONOMY;
4977             if ( ( !getControlPanel().isShowTaxonomyScientificNames() && !getControlPanel().isShowTaxonomyCode() && !getControlPanel()
4978                     .isShowTaxonomyCommonNames() ) ) {
4979                 if ( ( getControlPanel().isShowSequenceAcc() || getControlPanel().isShowGeneNames() || getControlPanel()
4980                         .isShowGeneSymbols() ) ) {
4981                     pri = DESCENDANT_SORT_PRIORITY.SEQUENCE;
4982                 }
4983                 else if ( getControlPanel().isShowNodeNames() ) {
4984                     pri = DESCENDANT_SORT_PRIORITY.NODE_NAME;
4985                 }
4986             }
4987             PhylogenyMethods.sortNodeDescendents( node, pri );
4988             setNodeInPreorderToNull();
4989             _phylogeny.externalNodesHaveChanged();
4990             _phylogeny.clearHashIdToNodeMap();
4991             _phylogeny.recalculateNumberOfExternalDescendants( true );
4992             resetNodeIdToDistToLeafMap();
4993             setEdited( true );
4994         }
4995         repaint();
4996     }
4997
4998     final private void switchDisplaygetPhylogenyGraphicsType() {
4999         switch ( getPhylogenyGraphicsType() ) {
5000             case RECTANGULAR:
5001                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5002                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.EURO_STYLE );
5003                 break;
5004             case EURO_STYLE:
5005                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5006                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.ROUNDED );
5007                 break;
5008             case ROUNDED:
5009                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5010                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CURVED );
5011                 break;
5012             case CURVED:
5013                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5014                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.TRIANGULAR );
5015                 break;
5016             case TRIANGULAR:
5017                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5018                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CONVEX );
5019                 break;
5020             case CONVEX:
5021                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5022                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.UNROOTED );
5023                 break;
5024             case UNROOTED:
5025                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5026                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.CIRCULAR );
5027                 break;
5028             case CIRCULAR:
5029                 setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5030                 getOptions().setPhylogenyGraphicsType( PHYLOGENY_GRAPHICS_TYPE.RECTANGULAR );
5031                 break;
5032             default:
5033                 throw new RuntimeException( "unkwnown display type: " + getPhylogenyGraphicsType() );
5034         }
5035         if ( getControlPanel().getDynamicallyHideData() != null ) {
5036             if ( getPhylogenyGraphicsType() == PHYLOGENY_GRAPHICS_TYPE.UNROOTED ) {
5037                 getControlPanel().getDynamicallyHideData().setEnabled( false );
5038             }
5039             else {
5040                 getControlPanel().getDynamicallyHideData().setEnabled( true );
5041             }
5042         }
5043         if ( isPhyHasBranchLengths() && ( getPhylogenyGraphicsType() != PHYLOGENY_GRAPHICS_TYPE.CIRCULAR ) ) {
5044             getControlPanel().setDrawPhylogramEnabled( true );
5045         }
5046         else {
5047             getControlPanel().setDrawPhylogramEnabled( false );
5048         }
5049         if ( getMainPanel().getMainFrame() == null ) {
5050             // Must be "E" applet version.
5051             ( ( ArchaeopteryxE ) ( ( MainPanelApplets ) getMainPanel() ).getApplet() )
5052                     .setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5053         }
5054         else {
5055             getMainPanel().getMainFrame().setSelectedTypeInTypeMenu( getPhylogenyGraphicsType() );
5056         }
5057     }
5058
5059     final void taxColor() {
5060         if ( ( _phylogeny == null ) || ( _phylogeny.getNumberOfExternalNodes() < 2 ) ) {
5061             return;
5062         }
5063         setWaitCursor();
5064         AptxUtil.colorPhylogenyAccordingToExternalTaxonomy( _phylogeny, this );
5065         _control_panel.setColorBranches( true );
5066         if ( _control_panel.getColorBranchesCb() != null ) {
5067             _control_panel.getColorBranchesCb().setSelected( true );
5068         }
5069         setArrowCursor();
5070         repaint();
5071     }
5072
5073     final void updateOvSettings() {
5074         switch ( getOptions().getOvPlacement() ) {
5075             case LOWER_LEFT:
5076                 setOvXPosition( OV_BORDER );
5077                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5078                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5079                 break;
5080             case LOWER_RIGHT:
5081                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5082                 setOvYPosition( ForesterUtil.roundToInt( getVisibleRect().height - OV_BORDER - getOvMaxHeight() ) );
5083                 setOvYStart( ForesterUtil.roundToInt( getOvYPosition() + ( getOvMaxHeight() / 2 ) ) );
5084                 break;
5085             case UPPER_RIGHT:
5086                 setOvXPosition( ForesterUtil.roundToInt( getVisibleRect().width - OV_BORDER - getOvMaxWidth() ) );
5087                 setOvYPosition( OV_BORDER );
5088                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5089                 break;
5090             default:
5091                 setOvXPosition( OV_BORDER );
5092                 setOvYPosition( OV_BORDER );
5093                 setOvYStart( ForesterUtil.roundToInt( OV_BORDER + ( getOvMaxHeight() / 2 ) ) );
5094                 break;
5095         }
5096     }
5097
5098     final void updateOvSizes() {
5099         if ( ( getWidth() > 1.05 * getVisibleRect().width ) || ( getHeight() > 1.05 * getVisibleRect().height ) ) {
5100             setOvOn( true );
5101             float l = getLongestExtNodeInfo();
5102             final float w_ratio = getOvMaxWidth() / getWidth();
5103             l *= w_ratio;
5104             final int ext_nodes = _phylogeny.getRoot().getNumberOfExternalNodes();
5105             setOvYDistance( getOvMaxHeight() / ( 2 * ext_nodes ) );
5106             float ov_xdist = 0;
5107             if ( !isNonLinedUpCladogram() && !isUniformBranchLengthsForCladogram() ) {
5108                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( ext_nodes ) );
5109             }
5110             else {
5111                 ov_xdist = ( ( getOvMaxWidth() - l ) / ( PhylogenyMethods.calculateMaxDepth( _phylogeny ) ) );
5112             }
5113             float ydist = ( float ) ( ( getOvMaxWidth() / ( ext_nodes * 2.0 ) ) );
5114             if ( ov_xdist < 0.0 ) {
5115                 ov_xdist = 0.0f;
5116             }
5117             if ( ydist < 0.0 ) {
5118                 ydist = 0.0f;
5119             }
5120             setOvXDistance( ov_xdist );
5121             final double height = _phylogeny.getHeight();
5122             if ( height > 0 ) {
5123                 final float ov_corr = ( float ) ( ( ( getOvMaxWidth() - l ) - getOvXDistance() ) / height );
5124                 setOvXcorrectionFactor( ov_corr > 0 ? ov_corr : 0 );
5125             }
5126             else {
5127                 setOvXcorrectionFactor( 0 );
5128             }
5129         }
5130         else {
5131             setOvOn( false );
5132         }
5133     }
5134
5135     final void updateSubSuperTreeButton() {
5136         if ( _subtree_index < 1 ) {
5137             getControlPanel().deactivateButtonToReturnToSuperTree();
5138         }
5139         else {
5140             getControlPanel().activateButtonToReturnToSuperTree( _subtree_index );
5141         }
5142     }
5143
5144     final void zoomInDomainStructure() {
5145         if ( _domain_structure_width < 2000 ) {
5146             _domain_structure_width *= 1.2;
5147         }
5148     }
5149
5150     final void zoomOutDomainStructure() {
5151         if ( _domain_structure_width > 20 ) {
5152             _domain_structure_width *= 0.8;
5153         }
5154     }
5155
5156     final private static void drawString( final int i, final double x, final double y, final Graphics2D g ) {
5157         g.drawString( String.valueOf( i ), ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5158     }
5159
5160     final private static void drawString( final String str, final double x, final double y, final Graphics2D g ) {
5161         g.drawString( str, ( int ) ( x + 0.5 ), ( int ) ( y + 0.5 ) );
5162     }
5163
5164     final private static boolean isSequenceEmpty( final Sequence seq ) {
5165         return ( seq.getAccession() == null ) && ForesterUtil.isEmpty( seq.getName() )
5166                 && ForesterUtil.isEmpty( seq.getSymbol() );
5167     }
5168
5169     final private static boolean isTaxonomyEmpty( final Taxonomy tax ) {
5170         return ( ( tax.getIdentifier() == null ) && ForesterUtil.isEmpty( tax.getTaxonomyCode() )
5171                 && ForesterUtil.isEmpty( tax.getCommonName() ) && ForesterUtil.isEmpty( tax.getScientificName() ) && tax
5172                 .getSynonyms().isEmpty() );
5173     }
5174
5175     final private static boolean plusPressed( final int key_code ) {
5176         return ( ( key_code == KeyEvent.VK_ADD ) || ( key_code == KeyEvent.VK_PLUS )
5177                 || ( key_code == KeyEvent.VK_EQUALS ) || ( key_code == KeyEvent.VK_SEMICOLON ) || ( key_code == KeyEvent.VK_1 ) );
5178     }
5179
5180     void setStatisticsForExpressionValues( final DescriptiveStatistics statistics_for_expression_values ) {
5181         _statistics_for_vector_data = statistics_for_expression_values;
5182     }
5183
5184     DescriptiveStatistics getStatisticsForExpressionValues() {
5185         return _statistics_for_vector_data;
5186     }
5187
5188     final private class SubtreeColorizationActionListener implements ActionListener {
5189
5190         JColorChooser _chooser;
5191         PhylogenyNode _node;
5192
5193         SubtreeColorizationActionListener( final JColorChooser chooser, final PhylogenyNode node ) {
5194             _chooser = chooser;
5195             _node = node;
5196         }
5197
5198         @Override
5199         public void actionPerformed( final ActionEvent e ) {
5200             final Color c = _chooser.getColor();
5201             if ( c != null ) {
5202                 colorizeSubtree( c, _node );
5203             }
5204         }
5205     }
5206
5207     public synchronized void setImageMap( final Hashtable<String, BufferedImage> image_map ) {
5208         getMainPanel().setImageMap( image_map );
5209     }
5210
5211     public synchronized Hashtable<String, BufferedImage> getImageMap() {
5212         return getMainPanel().getImageMap();
5213     }
5214 }