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